Compare commits

...

46 Commits

131 changed files with 3098 additions and 2431 deletions
+1 -1
View File
@@ -20,4 +20,4 @@ jobs:
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: "This PR has been closed for modifying protected files. See `CONTIBUTING.md` for more information."
comment: "This PR has been closed for modifying protected files. See `CONTRIBUTING.md` for more information."
+1 -1
View File
@@ -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>
[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).
+1 -1
View File
@@ -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>
[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).
+1 -1
View File
@@ -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>
[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.
+2 -2
View File
@@ -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>
[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).
@@ -67,7 +67,7 @@
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
+1 -1
View File
@@ -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>
[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).
+1 -1
View File
@@ -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>
[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).
+5 -5
View File
@@ -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>
[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.
@@ -14,7 +14,7 @@
* Daftar teman
* Teleportasi
* Sistem gacha
* Co-op *sebahagian* berfungsi
* Co-op *sebagian* berfungsi
* Memunculkan monster melalui konsol
* 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`
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
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
@@ -103,6 +103,6 @@ Anda bisa menemukan output jar di root folder proyek.
# Quick Troubleshooting
* 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
* Urutan startup: MongoDB > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Game
+108
View File
@@ -0,0 +1,108 @@
![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&forks=1&issues=1&language=1&logo=https%3A%2F%2Fs2.loli.net%2F2022%2F04%2F25%2FxOiJn7lCdcT5Mw1.png&name=1&owner=1&pulls=1&stargazers=1&theme=Light)
<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
+22 -23
View File
@@ -3,10 +3,10 @@
<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)をよくお読みください
## 現在機能している物
@@ -21,53 +21,52 @@
## クイックセットアップガイド
***:** サポートが必要な場合はGrasscutterの[Discord](https://discord.gg/T5vZU6UyeG)に参加してください.
***:** サポートが必要な場合はGrasscutterの[Discord](https://discord.gg/T5vZU6UyeG)に参加してください
### 動作環境
* [JAVAのバージョン17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
***:** サーバーを動作させるだけならjreのみで十分です。 開発をしたい場合JDKが必要になるかもしれません
***:** サーバーを動作させるだけならjreのみで十分です。 開発をしたい場合JDKが必要になるかもしれません
* [MongoDB](https://www.mongodb.com/try/download/community) (バージョン4.0以降を推奨)
* プロキシツール: [mitmproxy](https://mitmproxy.org/) (mitmdump, 推奨)[Fiddler Classic](https://telerik-fiddler.s3.amazonaws.com/fiddler/FiddlerSetup.exe)その他。
* プロキシツール: [mitmproxy](https://mitmproxy.org/) (mitmdump, 推奨)[Fiddler Classic](https://telerik-fiddler.s3.amazonaws.com/fiddler/FiddlerSetup.exe)その他。
### 起動方法
***:** もしサーバーをアップデートしたい場合は`config.json`を削除してから再生成してください
***:** もしサーバーをアップデートしたい場合は`config.json`を削除してから再生成してください
1. `grasscutter.jar`を入手する
- [action](https://github.com/Grasscutters/Grasscutter/actions) からダウンロードするか [自分でビルド](https://github.com/Grasscutters/Grasscutter#building)してください。
2. `grasscutter.jar` があるディレクトリに `resources` フォルダーを作成しそこに `BinOutput, ExcelBinOutput, Readables, Scripts, Subtitle, TextMap` を移動してください *(`resources` フォルダの中身の入手方法については [wiki](https://github.com/Grasscutters/Grasscutter/wiki) を参照してください.)*
3. コマンドプロンプトに`java -jar grasscutter.jar`を入力しGrasscutterを起動してください**このときMongoDBも実行する必要があります**
- [releases](https://github.com/Grasscutters/Grasscutter/releases/latest) か [action](https://github.com/Grasscutters/Grasscutter/actions) からダウンロードするか[自分でビルド](https://github.com/Grasscutters/Grasscutter#building)してください。
2. `grasscutter.jar` があるディレクトリに `resources` フォルダーを作成しそこに `BinOutput, ExcelBinOutput, Readables, Scripts, Subtitle, TextMap` を移動してください *(`resources` フォルダの中身の入手方法については [wiki](https://github.com/Grasscutters/Grasscutter/wiki) を参照してください.)*
3. コマンドプロンプトに`java -jar grasscutter.jar`を入力しGrasscutterを起動してください**このときMongoDBも実行する必要があります**
### クライアントとの接続
½. [サーバーコンソールコマンド](https://github.com/Grasscutters/Grasscutter/wiki/Commands#targeting)を使用してアカウントを作成してください。
½. [このコマンド](https://github.com/Grasscutters/Grasscutter/wiki/Commands#commands-for-server-admins)をサーバーコンソールから使用してアカウントを作成してください。
1. 通信内容をリダイレクトする: (どちらか一つを選択してください)
- mitmdump: `mitmdump -s proxy.py -k`
CA証明書を信頼する:
- CA証明書を信頼する:
**:** CA証明書は`%USERPROFILE%\.mitmproxy`に保存されているか、`http://mitm.it`からダウンロードできます。
- **:** CA証明書は`%USERPROFILE%\.mitmproxy`に保存されています。ダブルクリックして[インストール](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate)するか...
ダブルクリックして[インストール](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate)するか...
- コマンドライン経由でインストールします
- コマンドライン経由でインストールします
```shell
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
```
- Fiddler Classic: Fiddler Classicを起動し設定から`Decrypt https traffic`をオンにしてください。 (Tools -> Options -> Connections) に有るポート番号の設定を`8888`以外に設定してくださいその後この[スクリプト](https://github.com/Grasscutters/Grasscutter/wiki/Resources#fiddler-classic-jscript)をロードします
- Fiddler Classic: Fiddler Classicを起動し(Tools -> Options -> HTTPS)から`Decrypt https traffic`をオンにしてください。 (Tools -> Options -> Connections) に有るポート番号の設定を`8888`以外に設定してくださいその後この[スクリプト](https://github.com/Grasscutters/Grasscutter/wiki/Resources#fiddler-classic-jscript)をFiddlerScriptタブにコピペしてロードします
- [ホストファイル](https://github.com/Grasscutters/Grasscutter/wiki/Resources#hosts-file)
2. ネットワークプロキシを `127.0.0.1:(自分で設定したポート番号)` に設定してください。
- mitmproxyを使用した場合:プロキシの設定と証明書のインストールが終わった後、http://mitm.it/ でトラフィックがmitmproxyを通過しているか確認しましょう。
**`start_config.cmd`でJAVAのパスを指定している必要があります。 `start.cmd`でmitmdumpとサーバーをまとめて起動することが出来ます。**
**`start.cmd`でmitmdumpとサーバーをまとめて起動することが出来ます。ただ、事前に`start_config.cmd`でJAVAのパスを指定している必要があります。**
### ビルド
@@ -75,7 +74,7 @@ GrasscutterはGradleを使用して依存関係とビルドを処理していま
**要件:**
- [Java SE 開発キット - 17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
- [Java SE Development Kits - 17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
- [Git](https://git-scm.com/downloads)
##### Windows
@@ -96,13 +95,13 @@ chmod +x gradlew
./gradlew jar # コンパイル
```
コンパイルされたjarファイルはプロクトフォルダのルートに有ります
生成されたjarファイルはプロジェクトフォルダのルートに有ります
### コマンドリストは[wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)へ移動しました。
# トラブルシューティング
* コンパイルが失敗した場合JDKがインストールされているか確認してください (JDKの17以降と環境変数でJAVAのパスが設定されている必要があります)
* クライアントが接続できないログインできないエラーコード4206、 その他... - ほとんどの場合、プロキシ デーモンの設定が問題です。
Fiddlerを使用している場合はポートが8888以外に設定されていることを確認してください
* 起動シーケンス(順番): MongoDB > Grasscutter > プロキシツール (mitmdumpかfiddlerその他) > ゲーム
* コンパイルが失敗した場合JDKがインストールされているか確認してください(JDKのバージョンが17以降であることと、環境変数でJDKのパスが設定されている必要があります)
* クライアントが接続できないログインできないエラーコード4206・またその他場合、ほとんど、プロキシデーモンの設定が問題です。Fiddlerを使っている場合はデフォルトポートを8888以外の別のポートに変更してみてください。
Fiddlerを使用している場合はポートが8888以外に設定されていることを確認してください
* 起動シーケンス(順番): MongoDB > Grasscutter > プロキシツール (mitmdumpかfiddlerその他) > ゲーム
+1 -1
View File
@@ -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>
[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)을 주의 깊게 읽어주세요.
+1 -1
View File
@@ -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>
[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).
+1 -1
View File
@@ -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>
[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).
+1 -1
View File
@@ -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>
[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)。
+1 -1
View File
@@ -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>
[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)。
+1 -1
View File
@@ -43,7 +43,7 @@ sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
group = 'xyz.grasscutters'
version = '1.4.2'
version = '1.4.4-dev'
sourceCompatibility = 17
targetCompatibility = 17
Binary file not shown.
Vendored
+158 -112
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,78 +17,113 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -105,84 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+2
View File
@@ -0,0 +1,2 @@
call .\gradlew jar
pause
Vendored
+89 -100
View File
@@ -1,100 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@@ -6,7 +6,7 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
public final class GameConstants {
public static String VERSION = "3.1.0";
public static String VERSION = "3.2.0";
public static final int DEFAULT_TEAMS = 4;
public static final int MAX_TEAMS = 10;
@@ -293,8 +293,8 @@ public final class Grasscutter {
Grasscutter.getLogger().info("EOF detected.");
continue;
} catch (IOError e) {
Grasscutter.getLogger().error("An IO error occurred.", e);
continue;
Grasscutter.getLogger().error("An IO error occurred while trying to read from console.", e);
return;
}
isLastInterrupted = false;
@@ -33,21 +33,18 @@ public class CommandHelpers {
}
public static <T> List<String> parseIntParameters(List<String> args, @Nonnull T params, Map<Pattern, BiConsumer<T, Integer>> map) {
for (int i = args.size() - 1; i >= 0; i--) {
String arg = args.get(i).toLowerCase();
args.removeIf(arg -> {
var argL = arg.toLowerCase();
boolean deleteArg = false;
int argNum;
for (var entry : map.entrySet()) {
if ((argNum = matchIntOrNeg(entry.getKey(), arg)) != -1) {
int argNum = matchIntOrNeg(entry.getKey(), argL);
if (argNum != -1) {
entry.getValue().accept(params, argNum);
deleteArg = true;
break;
}
}
if (deleteArg) {
args.remove(i);
}
}
return deleteArg;
});
return args;
}
}
@@ -89,7 +89,7 @@ public final class CommandMap {
}
public List<Command> getAnnotationsAsList() {
return new LinkedList<>(this.annotations.values());
return new ArrayList<>(this.annotations.values());
}
public Map<String, Command> getAnnotations() {
@@ -102,7 +102,7 @@ public final class CommandMap {
* @return All command handlers as a list.
*/
public List<CommandHandler> getHandlersAsList() {
return new LinkedList<>(this.commands.values());
return new ArrayList<>(this.commands.values());
}
public Map<String, CommandHandler> getHandlers() {
@@ -234,8 +234,8 @@ public final class CommandMap {
// Parse message.
String[] split = rawMessage.split(" ");
List<String> args = new LinkedList<>(Arrays.asList(split));
String label = args.remove(0).toLowerCase();
String label = split[0].toLowerCase();
List<String> args = new ArrayList<>(Arrays.asList(split).subList(1, split.length));
String playerId = (player == null) ? consoleId : player.getAccount().getId();
// Check for special cases - currently only target command.
@@ -9,6 +9,7 @@ import emu.grasscutter.game.player.Player;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import static emu.grasscutter.utils.Language.translate;
@@ -78,9 +79,10 @@ public final class SendMailCommand implements CommandHandler {
Grasscutter.getGameServer().getPlayerByUid(mailBuilder.recipient, true).sendMail(mailBuilder.mail);
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_done", mailBuilder.recipient));
} else {
for (Player player : DatabaseHelper.getAllPlayers()) {
Grasscutter.getGameServer().getPlayerByUid(player.getUid(), true).sendMail(mailBuilder.mail);
}
DatabaseHelper.getByGameClass(Player.class).forEach(player -> {
var onlinePlayer = Grasscutter.getGameServer().getPlayerByUid(player.getUid(), false);
Objects.requireNonNullElse(onlinePlayer, player).sendMail(mailBuilder.mail);
});
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_all_done"));
}
mailBeingConstructed.remove(senderId);
@@ -67,9 +67,10 @@ public final class SetStatsCommand implements CommandHandler {
// Compatibility aliases
this.stats.put("mhp", this.stats.get("maxhp"));
this.stats.put("hp", new Stat(FightProperty.FIGHT_PROP_CUR_HP)); // Overrides FIGHT_PROP_HP
this.stats.put("atk", new Stat(FightProperty.FIGHT_PROP_CUR_ATTACK)); // Overrides FIGHT_PROP_ATTACK
this.stats.put("atkb", new Stat(FightProperty.FIGHT_PROP_BASE_ATTACK)); // This doesn't seem to get used to recalculate ATK, so it's only useful for stuff like Bennett's buff.
this.stats.put("hp", this.stats.get("_cur_hp")); // Overrides FIGHT_PROP_HP
this.stats.put("atk", this.stats.get("_cur_attack")); // Overrides FIGHT_PROP_ATTACK
this.stats.put("def", this.stats.get("_cur_defense")); // Overrides FIGHT_PROP_DEFENSE
this.stats.put("atkb", this.stats.get("_base_attack")); // This doesn't seem to get used to recalculate ATK, so it's only useful for stuff like Bennett's buff.
this.stats.put("eanemo", this.stats.get("anemo%"));
this.stats.put("ecryo", this.stats.get("cryo%"));
this.stats.put("edendro", this.stats.get("dendro%"));
@@ -1,5 +1,6 @@
package emu.grasscutter.config;
import ch.qos.logback.classic.Level;
import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode;
@@ -101,6 +102,7 @@ public class ConfigContainer {
public Game game = new Game();
public Dispatch dispatch = new Dispatch();
public DebugMode debugMode = new DebugMode();
}
public static class Language {
@@ -150,6 +152,10 @@ public class ConfigContainer {
public int kcpInterval = 20;
/* Controls whether packets should be logged in console or not */
public ServerDebugMode logPackets = ServerDebugMode.NONE;
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
public Boolean isShowPacketPayload = false;
/* Show annoying loop packets or no */
public Boolean isShowLoopPackets = false;
public GameOptions gameOptions = new GameOptions();
public JoinOptions joinOptions = new JoinOptions();
@@ -163,9 +169,33 @@ public class ConfigContainer {
public String defaultName = "Grasscutter";
/* Controls whether http requests should be logged in console or not */
public ServerDebugMode logRequests = ServerDebugMode.NONE;
}
/* Debug options container, used when jar launch argument is -debug | -debugall and override default values
* (see StartupArguments.enableDebug) */
public static class DebugMode {
/* Log level of the main server code (works only with -debug arg) */
public Level serverLoggerLevel = Level.DEBUG;
/* Log level of the third-party services (works only with -debug arg):
javalin, quartz, reflections, jetty, mongodb.driver*/
public Level servicesLoggersLevel = Level.INFO;
/* Controls whether packets should be logged in console or not */
public ServerDebugMode logPackets = ServerDebugMode.ALL;
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
public Boolean isShowPacketPayload = false;
/* Show annoying loop packets or no */
public Boolean isShowLoopPackets = false;
/* Controls whether http requests should be logged in console or not */
public ServerDebugMode logRequests = ServerDebugMode.ALL;
}
public static class Encryption {
public boolean useEncryption = true;
/* Should 'https' be appended to URLs? */
@@ -38,6 +38,7 @@ public final class Configuration extends ConfigContainer {
public static final HTTP HTTP_INFO = config.server.http;
public static final Game GAME_INFO = config.server.game;
public static final Dispatch DISPATCH_INFO = config.server.dispatch;
public static final DebugMode DEBUG_MODE_INFO = config.server.debugMode;
public static final Encryption HTTP_ENCRYPTION = config.server.http.encryption;
public static final Policies HTTP_POLICIES = config.server.http.policies;
@@ -5,6 +5,8 @@ import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.JsonUtils;
import emu.grasscutter.utils.TsvUtils;
import lombok.val;
import java.io.FileNotFoundException;
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() {
try {
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.SpawnGroupEntry;
import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.utils.FileUtils;
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.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntArraySet;
@@ -23,6 +26,8 @@ import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.regex.Pattern;
import java.util.stream.Stream;
@@ -32,8 +37,9 @@ import static emu.grasscutter.utils.Language.translate;
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() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
@@ -51,6 +57,25 @@ public class ResourceLoader {
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;
public static void loadAll() {
if (loadedAll) return;
@@ -86,48 +111,66 @@ public class ResourceLoader {
}
public static void loadResources(boolean doReload) {
for (Class<?> resourceDefinition : getResourceDefClasses()) {
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
long startTime = System.nanoTime();
val errors = new ConcurrentLinkedQueue<Pair<String, Exception>>(); // Logger in a parallel stream will deadlock
if (type == null) {
continue;
}
getResourceDefClassesPrioritySets().forEach(classes -> {
classes.stream()
.parallel().unordered()
.forEach(c -> {
val type = c.getAnnotation(ResourceType.class);
if (type == null) return;
@SuppressWarnings("rawtypes")
Int2ObjectMap map = GameData.getMapByResourceDef(resourceDefinition);
val map = GameData.getMapByResourceDef(c);
if (map == null) return;
if (map == null) {
continue;
}
try {
loadFromResource(resourceDefinition, type, map, doReload);
} catch (Exception e) {
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e.getLocalizedMessage());
}
}
try {
loadFromResource(c, type, map, doReload);
} catch (Exception e) {
errors.add(Pair.of(Arrays.toString(type.name()), e));
}
});
});
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")
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()) {
loadFromResource(c, name, map);
loadFromResource(c, FileUtils.getExcelPath(name), map);
}
loadedResources.add(c.getSimpleName());
Grasscutter.getLogger().debug("Loaded " + map.size() + " " + c.getSimpleName() + "s.");
loadedResources.add(simpleName);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected static <T> void loadFromResource(Class<T> c, String fileName, Int2ObjectMap map) throws Exception {
List<T> list = JsonUtils.loadToList(getResourcePath("ExcelBinOutput/" + fileName), c);
for (T o : list) {
protected static <T> void loadFromResource(Class<T> c, Path filename, Int2ObjectMap map) throws Exception {
val results = switch (FileUtils.getFileExtension(filename)) {
case "json" -> JsonUtils.loadToList(filename, c);
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;
res.onLoad();
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()
@@ -2,6 +2,8 @@ package emu.grasscutter.data;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.stream.Stream;
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourceType {
@@ -28,5 +30,9 @@ public @interface ResourceType {
public int value() {
return value;
}
public static List<LoadPriority> getInOrder() {
return Stream.of(LoadPriority.values()).sorted((x, y) -> y.value() - x.value()).toList();
}
}
}
@@ -13,18 +13,15 @@ import java.util.List;
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityWatcherData extends GameResource {
@Getter(onMethod = @__(@Override))
int id;
int rewardID;
int progress;
WatcherTrigger triggerConfig;
@Override
public int getId() {
return this.id;
}
@Override
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);
}
@@ -44,6 +44,7 @@ public class AvatarData extends GameResource {
private float criticalHurt;
private List<PropGrowCurve> propGrowCurves;
@Getter(onMethod = @__(@Override))
private int id;
// Transient
@@ -60,11 +61,6 @@ public class AvatarData extends GameResource {
@Getter private int nameCardRewardId;
@Getter private int nameCardId;
@Override
public int getId() {
return this.id;
}
public float getBaseHp(int level) {
try {
return this.hpBase * this.hpGrowthCurve[level - 1];
@@ -7,26 +7,18 @@ import emu.grasscutter.game.props.ElementType;
import lombok.Getter;
@ResourceType(name = "AvatarSkillExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
@Getter
public class AvatarSkillData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
@Getter private float cdTime;
@Getter private int costElemVal;
@Getter private int maxChargeNum;
@Getter private int triggerID;
@Getter private boolean isAttackCameraLock;
@Getter private int proudSkillGroupId;
@Getter private ElementType costElemType;
@Getter private long nameTextMapHash;
@Getter private long descTextMapHash;
@Getter private String abilityName;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
}
private float cdTime;
private int costElemVal;
private int maxChargeNum;
private int triggerID;
private boolean isAttackCameraLock;
private int proudSkillGroupId;
private ElementType costElemType;
private long nameTextMapHash;
private long descTextMapHash;
private String abilityName;
}
@@ -18,31 +18,27 @@ import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
@ResourceType(name = "AvatarSkillDepotExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter
public class AvatarSkillDepotData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
@Getter private int energySkill;
@Getter private int attackModeSkill;
private int energySkill;
private int attackModeSkill;
@Getter private List<Integer> skills;
@Getter private List<Integer> subSkills;
@Getter private List<String> extraAbilities;
@Getter private List<Integer> talents;
@Getter private List<InherentProudSkillOpens> inherentProudSkillOpens;
private List<Integer> skills;
private List<Integer> subSkills;
private List<String> extraAbilities;
private List<Integer> talents;
private List<InherentProudSkillOpens> inherentProudSkillOpens;
@Getter private String talentStarName;
@Getter private String skillDepotAbilityGroup;
private String talentStarName;
private String skillDepotAbilityGroup;
// Transient
@Getter private AvatarSkillData energySkillData;
@Getter private ElementType elementType;
@Getter private IntList abilities;
@Getter private int talentCostItemId;
@Override
public int getId() {
return this.id;
}
private AvatarSkillData energySkillData;
private ElementType elementType;
private IntList abilities;
private int talentCostItemId;
public void setAbilities(AbilityEmbryoEntry info) {
this.abilities = new IntArrayList(info.getAbilities().length);
@@ -77,9 +73,10 @@ public class AvatarSkillDepotData extends GameResource {
.ifPresent(itemId -> this.talentCostItemId = itemId);
}
@Getter
public static class InherentProudSkillOpens {
@Getter private int proudSkillGroupId;
@Getter private int needAvatarPromoteLevel;
private int proudSkillGroupId;
private int needAvatarPromoteLevel;
}
public IntStream getSkillsAndEnergySkill() {
@@ -9,65 +9,61 @@ import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.BattlePassMissionRefreshType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
@ResourceType(name = {"BattlePassMissionExcelConfigData.json"})
@Getter
public class BattlePassMissionData extends GameResource {
private int addPoint;
@Getter(onMethod = @__(@Override))
private int id;
private int addPoint;
private int scheduleId;
private int progress;
private TriggerConfig triggerConfig;
private BattlePassMissionRefreshType refreshType;
private transient Set<Integer> mainParams;
@Override
public int getId() {
return this.id;
}
public WatcherTriggerType getTriggerType() {
return this.getTriggerConfig().getTriggerType();
}
public boolean isCycleRefresh() {
return getRefreshType() == null || getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE;
}
public boolean isValidRefreshType() {
return getRefreshType() == null ||
getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE ||
getScheduleId() == 2701;
return this.getTriggerConfig().getTriggerType();
}
public boolean isCycleRefresh() {
return getRefreshType() == null || getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE;
}
public boolean isValidRefreshType() {
return getRefreshType() == null ||
getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE ||
getScheduleId() == 2701;
}
@Override
public void onLoad() {
if (this.getTriggerConfig() != null && getTriggerConfig().getParamList()[0].length() > 0) {
this.mainParams = Arrays.stream(getTriggerConfig().getParamList()[0].split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet());
}
if (this.getTriggerConfig() != null) {
var params = getTriggerConfig().getParamList()[0];
if ((params != null) && !params.isEmpty()) {
this.mainParams = Arrays.stream(params.split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet());
}
}
}
@Getter
public static class TriggerConfig {
private WatcherTriggerType triggerType;
private String[] paramList;
private WatcherTriggerType triggerType;
private String[] paramList;
}
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
var protoBuilder = emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
protoBuilder
.setMissionId(getId())
.setTotalProgress(this.getProgress())
.setRewardBattlePassPoint(this.getAddPoint())
.setMissionStatus(MissionStatus.MISSION_STATUS_UNFINISHED)
.setMissionType(this.getRefreshType() == null ? 0 : this.getRefreshType().getValue());
return protoBuilder.build();
}
var protoBuilder = emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
protoBuilder
.setMissionId(getId())
.setTotalProgress(this.getProgress())
.setRewardBattlePassPoint(this.getAddPoint())
.setMissionStatus(MissionStatus.MISSION_STATUS_UNFINISHED)
.setMissionType(this.getRefreshType() == null ? 0 : this.getRefreshType().getValue());
return protoBuilder.build();
}
}
@@ -7,40 +7,39 @@ import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "BlossomRefreshExcelConfigData.json")
@Getter
public class BlossomRefreshExcelConfigData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
// Map details
@Getter private long nameTextMapHash;
@Getter private long descTextMapHash;
@Getter private String icon;
@Getter private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK
private long nameTextMapHash;
private long descTextMapHash;
private String icon;
private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK
// Refresh details
@Getter private String refreshType; // Leyline blossoms, magical ore outcrops
@Getter private int refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city, 4 for magical ore for each city)
@Getter private String refreshTime; // Server time-of-day to refresh at
@Getter private RefreshCond[] refreshCondVec; // AR requirements etc.
private String refreshType; // Leyline blossoms, magical ore outcrops
private int refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city, 4 for magical ore for each city)
private String refreshTime; // Server time-of-day to refresh at
private RefreshCond[] refreshCondVec; // AR requirements etc.
@Getter private int cityId;
@Getter private int blossomChestId; // 1 for mora, 2 for exp
@Getter private Drop[] dropVec;
private int cityId;
private int blossomChestId; // 1 for mora, 2 for exp
private Drop[] dropVec;
// Unknown details
// @Getter private int reviseLevel;
// @Getter private int campUpdateNeedCount; // Always 1 if specified
@Override
public int getId() {
return id;
}
@Getter
public static class Drop {
@Getter int dropId;
@Getter int previewReward;
int dropId;
int previewReward;
}
@Getter
public static class RefreshCond {
@Getter String type;
@Getter List<Integer> param;
String type;
List<Integer> param;
}
}
@@ -8,27 +8,23 @@ import lombok.Setter;
import lombok.experimental.FieldDefaults;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ResourceType(name = "ChapterExcelConfigData.json")
@Getter
@Setter
@Setter // TODO: remove on next API break
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ChapterData extends GameResource {
@Getter(onMethod = @__(@Override))
int id;
int beginQuestId;
int endQuestId;
int needPlayerLevel;
// Why public? TODO: privatise next API break
public static final Map<Integer, ChapterData> beginQuestChapterMap = new HashMap<>();
public static final Map<Integer, ChapterData> endQuestChapterMap = new HashMap<>();
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
beginQuestChapterMap.put(beginQuestId, this);
@@ -22,9 +22,4 @@ public class CityData extends GameResource {
public int getId() {
return this.cityId;
}
@Override
public void onLoad() {
super.onLoad();
}
}
@@ -7,18 +7,15 @@ import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = {"AnimalCodexExcelConfigData.json"})
@Getter
public class CodexAnimalData extends GameResource {
@Getter(onMethod = @__(@Override))
private int Id;
@Getter private String type;
@Getter private int describeId;
@Getter private int sortOrder;
private String type;
private int describeId;
private int sortOrder;
@SerializedName(value="countType", alternate={"OCCLHPBCDGL"})
@Getter private CountType countType;
@Override
public int getId() {
return Id;
}
private CountType countType;
public enum CountType {
CODEX_COUNT_TYPE_KILL,
@@ -8,19 +8,15 @@ import lombok.Getter;
import java.util.List;
@ResourceType(name = {"CompoundExcelConfigData.json"},loadPriority = ResourceType.LoadPriority.LOW)
@Getter
public class CompoundData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
@Override
public int getId() {return this.id;}
@Getter private int groupId;
@Getter private int rankLevel;
@Getter private boolean isDefaultUnlocked;
@Getter private int costTime;
@Getter private int queueSize;
@Getter private List<ItemParamData> inputVec;
@Getter private List<ItemParamData> outputVec;
@Override
public void onLoad(){}
private int groupId;
private int rankLevel;
private boolean isDefaultUnlocked;
private int costTime;
private int queueSize;
private List<ItemParamData> inputVec;
private List<ItemParamData> outputVec;
}
@@ -9,21 +9,15 @@ import emu.grasscutter.data.common.ItemParamData;
import lombok.Getter;
@ResourceType(name = {"CookRecipeExcelConfigData.json"}, loadPriority = LoadPriority.LOW)
@Getter
public class CookRecipeData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
@Getter private int rankLevel;
@Getter boolean isDefaultUnlocked;
@Getter int maxProficiency;
private int rankLevel;
private boolean isDefaultUnlocked;
private int maxProficiency;
@Getter List<ItemParamData> qualityOutputVec;
@Getter List<ItemParamData> inputVec;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
}
private List<ItemParamData> qualityOutputVec;
private List<ItemParamData> inputVec;
}
@@ -7,9 +7,11 @@ import emu.grasscutter.data.ResourceType;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
@ResourceType(name = "DailyDungeonConfigData.json")
public class DailyDungeonData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private int[] monday;
private int[] tuesday;
@@ -26,11 +28,6 @@ public class DailyDungeonData extends GameResource {
this.map = new Int2ObjectOpenHashMap<>();
}
@Override
public int getId() {
return this.id;
}
public int[] getDungeonsByDay(int day) {
return map.getOrDefault(day, empty);
}
@@ -3,51 +3,28 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType;
import lombok.Getter;
@ResourceType(name = "DungeonExcelConfigData.json")
public class DungeonData extends GameResource {
private int id;
private int sceneId;
private int showLevel;
private int passRewardPreviewID;
private String involveType; // TODO enum
private RewardPreviewData previewData;
@Getter(onMethod = @__(@Override))
private int id;
@Getter private int sceneId;
@Getter private int showLevel;
private int passRewardPreviewID;
private String involveType; // TODO enum
private int statueCostID;
private int statueCostCount;
@Override
public int getId() {
return this.id;
}
private RewardPreviewData previewData;
public int getSceneId() {
return sceneId;
}
public int getShowLevel() {
return showLevel;
}
@Getter private int statueCostID;
@Getter private int statueCostCount;
public RewardPreviewData getRewardPreview() {
return previewData;
}
public RewardPreviewData getRewardPreview() {return previewData;}
public int getStatueCostID() {
return statueCostID;
}
public int getStatueCostCount() {
return statueCostCount;
}
@Override
public void onLoad() {
if (this.passRewardPreviewID > 0) {
this.previewData = GameData.getRewardPreviewDataMap().get(this.passRewardPreviewID);
}
}
@Override
public void onLoad() {
if (this.passRewardPreviewID > 0) {
this.previewData = GameData.getRewardPreviewDataMap().get(this.passRewardPreviewID);
}
}
}
@@ -7,19 +7,10 @@ import lombok.Setter;
@ResourceType(name = "DungeonEntryExcelConfigData.json")
@Getter
@Setter
@Setter // TODO: remove this next API break
public class DungeonEntryData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private int dungeonEntryId;
private int sceneId;
private int id;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
}
}
@@ -6,9 +6,12 @@ import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.ItemParamData;
import lombok.Getter;
@ResourceType(name = {"ForgeExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST)
@Getter
public class ForgeData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private int playerLevel;
private int forgeType;
@@ -21,57 +24,4 @@ public class ForgeData extends GameResource {
private int priority;
private int forgePoint;
private List<ItemParamData> materialItems;
@Override
public int getId() {
return this.id;
}
public int getPlayerLevel() {
return playerLevel;
}
public int getForgeType() {
return forgeType;
}
public int getResultItemId() {
return resultItemId;
}
public int getResultItemCount() {
return resultItemCount;
}
public int getForgeTime() {
return forgeTime;
}
public int getQueueNum() {
return queueNum;
}
public int getScoinCost() {
return scoinCost;
}
public int getPriority() {
return priority;
}
public int getForgePoint() {
return forgePoint;
}
public List<ItemParamData> getMaterialItems() {
return materialItems;
}
public int getShowItemId() {
return showItemId;
}
@Override
public void onLoad() {
}
}
@@ -3,9 +3,12 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.EntityType;
import lombok.Getter;
@ResourceType(name = "GadgetExcelConfigData.json")
@Getter
public class GadgetData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private EntityType type;
@@ -15,42 +18,4 @@ public class GadgetData extends GameResource {
private String itemJsonName;
private long nameTextMapHash;
private int campID;
@Override
public int getId() {
return this.id;
}
public EntityType getType() {
return type;
}
public String getJsonName() {
return jsonName;
}
public boolean isInteractive() {
return isInteractive;
}
public String[] getTags() {
return tags;
}
public String getItemJsonName() {
return itemJsonName;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public int getCampID() {
return campID;
}
@Override
public void onLoad() {
}
}
@@ -29,9 +29,4 @@ public class HomeWorldLevelData extends GameResource {
public int getId() {
return level;
}
@Override
public void onLoad() {
super.onLoad();
}
}
@@ -4,7 +4,6 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@@ -14,6 +13,7 @@ import java.util.List;
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class InvestigationMonsterData extends GameResource {
@Getter(onMethod = @__(@Override))
int id;
int cityId;
List<Integer> monsterIdList;
@@ -23,10 +23,6 @@ public class InvestigationMonsterData extends GameResource {
String monsterCategory;
CityData cityData;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
@@ -25,6 +25,7 @@ import lombok.Getter;
@Getter
public class ItemData extends GameResource {
// Main
@Getter(onMethod = @__(@Override))
private int id;
private int stackLimit = 1;
private int maxUseCount;
@@ -81,17 +82,12 @@ public class ItemData extends GameResource {
private List<Integer> furnType;
private List<Integer> furnitureGadgetID;
@SerializedName(value="roomSceneId", alternate={"DANFGGLKLNO", "JFDLJGDFIGL", "OHIANNAEEAK"})
@SerializedName(value="roomSceneId", alternate={"BMEPAMCNABE", "DANFGGLKLNO", "JFDLJGDFIGL", "OHIANNAEEAK"})
private int roomSceneId;
// Custom
private transient IntSet addPropLevelSet;
@Override
public int getId() {
return this.id;
}
public WeaponProperty[] getWeaponProperties() {
return this.weaponProp;
}
@@ -1,19 +1,28 @@
package emu.grasscutter.data.excels;
import java.util.List;
import java.util.Set;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.MonsterType;
import lombok.Getter;
@ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW)
@Getter
public class MonsterData extends GameResource {
private int id;
private String monsterName;
static public Set<FightProperty> definedFightProperties = Set.of(FightProperty.FIGHT_PROP_BASE_HP, FightProperty.FIGHT_PROP_BASE_ATTACK, FightProperty.FIGHT_PROP_BASE_DEFENSE, FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, FightProperty.FIGHT_PROP_FIRE_SUB_HURT, FightProperty.FIGHT_PROP_ELEC_SUB_HURT, FightProperty.FIGHT_PROP_WATER_SUB_HURT, FightProperty.FIGHT_PROP_GRASS_SUB_HURT, FightProperty.FIGHT_PROP_WIND_SUB_HURT, FightProperty.FIGHT_PROP_ROCK_SUB_HURT, FightProperty.FIGHT_PROP_ICE_SUB_HURT);
@Getter(onMethod = @__(@Override))
private int id;
private String monsterName;
private MonsterType type;
private String serverScript;
private List<Integer> affix;
@@ -28,9 +37,14 @@ public class MonsterData extends GameResource {
private int describeId;
private int combatBGMLevel;
private int entityBudgetLevel;
private float hpBase;
private float attackBase;
private float defenseBase;
@SerializedName("hpBase")
private float baseHp;
@SerializedName("attackBase")
private float baseAttack;
@SerializedName("defenseBase")
private float baseDefense;
private float fireSubHurt;
private float elecSubHurt;
private float grassSubHurt;
@@ -42,159 +56,49 @@ public class MonsterData extends GameResource {
private List<PropGrowCurve> propGrowCurves;
private long nameTextMapHash;
private int campID;
// Transient
private int weaponId;
private MonsterDescribeData describeData;
@Override
public int getId() {
return this.id;
}
public String getMonsterName() {
return monsterName;
}
public MonsterType getType() {
return type;
}
public float getFightProperty(FightProperty prop) {
return switch (prop) {
case FIGHT_PROP_BASE_HP -> this.baseHp;
case FIGHT_PROP_BASE_ATTACK -> this.baseAttack;
case FIGHT_PROP_BASE_DEFENSE -> this.baseDefense;
case FIGHT_PROP_PHYSICAL_SUB_HURT -> this.physicalSubHurt;
case FIGHT_PROP_FIRE_SUB_HURT -> this.fireSubHurt;
case FIGHT_PROP_ELEC_SUB_HURT -> this.elecSubHurt;
case FIGHT_PROP_WATER_SUB_HURT -> this.waterSubHurt;
case FIGHT_PROP_GRASS_SUB_HURT -> this.grassSubHurt;
case FIGHT_PROP_WIND_SUB_HURT -> this.windSubHurt;
case FIGHT_PROP_ROCK_SUB_HURT -> this.rockSubHurt;
case FIGHT_PROP_ICE_SUB_HURT -> this.iceSubHurt;
default -> 0f;
};
}
public String getServerScript() {
return serverScript;
}
@Override
public void onLoad() {
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId());
public List<Integer> getAffix() {
return affix;
}
for (int id : this.equips) {
if (id == 0) {
continue;
}
GadgetData gadget = GameData.getGadgetDataMap().get(id);
if (gadget == null) {
continue;
}
if (gadget.getItemJsonName().equals("Default_MonsterWeapon")) {
this.weaponId = id;
}
}
}
public String getAi() {
return ai;
}
public int[] getEquips() {
return equips;
}
public List<HpDrops> getHpDrops() {
return hpDrops;
}
public int getKillDropId() {
return killDropId;
}
public String getExcludeWeathers() {
return excludeWeathers;
}
public int getFeatureTagGroupID() {
return featureTagGroupID;
}
public int getMpPropID() {
return mpPropID;
}
public String getSkin() {
return skin;
}
public int getDescribeId() {
return describeId;
}
public int getCombatBGMLevel() {
return combatBGMLevel;
}
public int getEntityBudgetLevel() {
return entityBudgetLevel;
}
public float getBaseHp() {
return hpBase;
}
public float getBaseAttack() {
return attackBase;
}
public float getBaseDefense() {
return defenseBase;
}
public float getElecSubHurt() {
return elecSubHurt;
}
public float getGrassSubHurt() {
return grassSubHurt;
}
public float getWaterSubHurt() {
return waterSubHurt;
}
public float getWindSubHurt() {
return windSubHurt;
}
public float getIceSubHurt() {
return iceSubHurt;
}
public float getPhysicalSubHurt() {
return physicalSubHurt;
}
public List<PropGrowCurve> getPropGrowCurves() {
return propGrowCurves;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public int getCampID() {
return campID;
}
public MonsterDescribeData getDescribeData() {
return describeData;
}
public int getWeaponId() {
return weaponId;
}
@Override
public void onLoad() {
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId());
for (int id : this.equips) {
if (id == 0) {
continue;
}
GadgetData gadget = GameData.getGadgetDataMap().get(id);
if (gadget == null) {
continue;
}
if (gadget.getItemJsonName().equals("Default_MonsterWeapon")) {
this.weaponId = id;
}
}
}
public class HpDrops {
private int DropId;
private int HpPercent;
public int getDropId(){
return this.DropId;
}
public int getHpPercent(){
return this.HpPercent;
}
}
@Getter
public class HpDrops {
private int DropId;
private int HpPercent;
}
}
@@ -3,33 +3,14 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import lombok.Getter;
@ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter
public class MonsterDescribeData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private long nameTextMapHash;
private int titleID;
private int specialNameLabID;
@Override
public int getId() {
return id;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public int getTitleID() {
return titleID;
}
public int getSpecialNameLabID() {
return specialNameLabID;
}
@Override
public void onLoad() {
}
}
@@ -10,12 +10,8 @@ import lombok.experimental.FieldDefaults;
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class MusicGameBasicData extends GameResource {
@Getter(onMethod = @__(@Override))
int id;
int musicID;
int musicLevel;
@Override
public int getId() {
return this.id;
}
}
@@ -2,12 +2,15 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "NpcExcelConfigData.json")
@Getter
public class NpcData extends GameResource {
private int id;
private String jsonName;
@Getter(onMethod = @__(@Override))
private int id;
private String jsonName;
private String alias;
private String scriptDataPath;
private String luaDataPath;
@@ -19,54 +22,4 @@ public class NpcData extends GameResource {
private long nameTextMapHash;
private int campID;
@Override
public int getId() {
return this.id;
}
public String getJsonName() {
return jsonName;
}
public String getAlias() {
return alias;
}
public String getScriptDataPath() {
return scriptDataPath;
}
public String getLuaDataPath() {
return luaDataPath;
}
public boolean isIsInteractive() {
return isInteractive;
}
public boolean isHasMove() {
return hasMove;
}
public String getDyePart() {
return dyePart;
}
public String getBillboardIcon() {
return billboardIcon;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public int getCampID() {
return campID;
}
@Override
public void onLoad() {
}
}
@@ -3,15 +3,14 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ResourceType(name = "OpenStateConfigData.json", loadPriority = ResourceType.LoadPriority.HIGHEST)
public class OpenStateData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
@Getter private boolean defaultState;
@Getter private boolean allowClientOpen;
@@ -32,11 +31,6 @@ public class OpenStateData extends GameResource {
OPEN_STATE_COND_PARENT_QUEST;
}
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
// Add this open state to the global list.
@@ -11,18 +11,13 @@ import java.util.List;
@ResourceType(name = "PersonalLineExcelConfigData.json")
@Getter
@Setter
@Setter // TODO: remove setters next API break
@FieldDefaults(level = AccessLevel.PRIVATE)
public class PersonalLineData extends GameResource {
@Getter(onMethod = @__(@Override))
int id;
int avatarID;
List<Integer> preQuestId;
int startQuestId;
int chapterId;
@Override
public int getId() {
return this.id;
}
}
@@ -1,46 +1,23 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@ResourceType(name = "ReliquaryAffixExcelConfigData.json")
@Getter
public class ReliquaryAffixData extends GameResource {
private int id;
private int depotId;
private int groupId;
private FightProperty propType;
private float propValue;
private int weight;
private int upgradeWeight;
@Override
public int getId() {
return id;
}
public int getDepotId() {
return depotId;
}
public int getGroupId() {
return groupId;
}
public float getPropValue() {
return propValue;
}
public int getWeight() {
return weight;
}
public int getUpgradeWeight() {
return upgradeWeight;
}
public FightProperty getFightProp() {
return propType;
}
@Getter(onMethod = @__(@Override))
private int id;
private int depotId;
private int groupId;
@SerializedName("propType")
private FightProperty fightProp;
private float propValue;
private int weight;
private int upgradeWeight;
}
@@ -7,36 +7,19 @@ import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
@ResourceType(name = "ReliquaryLevelExcelConfigData.json")
public class ReliquaryLevelData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private Int2FloatMap propMap;
private int rank;
private int level;
private int exp;
@Getter private int rank;
@Getter private int level;
@Getter private int exp;
private List<RelicLevelProperty> addProps;
@Override
public int getId() {
return this.id;
}
public int getRank() {
return rank;
}
public int getLevel() {
return level;
}
public int getExp() {
return exp;
}
public float getPropValue(FightProperty prop) {
return getPropValue(prop.getId());
}
@@ -54,16 +37,9 @@ public class ReliquaryLevelData extends GameResource {
}
}
@Getter
public class RelicLevelProperty {
private String propType;
private float value;
public String getPropType() {
return propType;
}
public float getValue() {
return value;
}
}
}
@@ -1,31 +1,20 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@ResourceType(name = "ReliquaryMainPropExcelConfigData.json")
@Getter
public class ReliquaryMainPropData extends GameResource {
private int id;
private int propDepotId;
private FightProperty propType;
private int weight;
@Override
public int getId() {
return id;
}
public int getPropDepotId() {
return propDepotId;
}
public int getWeight() {
return weight;
}
@Getter(onMethod = @__(@Override))
private int id;
public FightProperty getFightProp() {
return propType;
}
private int propDepotId;
@SerializedName("propType")
private FightProperty fightProp;
private int weight;
}
@@ -1,43 +1,35 @@
package emu.grasscutter.data.excels;
import java.util.Arrays;
import java.util.List;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType;
import lombok.Getter;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.common.ItemParamStringData;
@ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH)
public class RewardPreviewData extends GameResource {
private int id;
private ItemParamStringData[] previewItems;
private ItemParamData[] previewItemsArray;
@Override
public int getId() {
return this.id;
}
@Getter(onMethod = @__(@Override))
private int id;
private ItemParamStringData[] previewItems;
private ItemParamData[] previewItemsArray;
public ItemParamData[] getPreviewItems() {
return previewItemsArray;
}
public ItemParamData[] getPreviewItems() {
return previewItemsArray;
}
@Override
public void onLoad() {
if (this.previewItems != null && this.previewItems.length > 0) {
this.previewItemsArray = Arrays.stream(this.previewItems)
.filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty())
.map(ItemParamStringData::toItemParamData)
.toArray(size -> new ItemParamData[size]);
} else {
this.previewItemsArray = new ItemParamData[0];
}
}
@Override
public void onLoad() {
if (this.previewItems != null && this.previewItems.length > 0) {
this.previewItemsArray = Arrays.stream(this.previewItems)
.filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty())
.map(ItemParamStringData::toItemParamData)
.toArray(size -> new ItemParamData[size]);
} else {
this.previewItemsArray = new ItemParamData[0];
}
}
}
@@ -1,32 +1,19 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType;
import lombok.Getter;
@ResourceType(name = "SceneExcelConfigData.json")
@Getter
public class SceneData extends GameResource {
private int id;
private SceneType type;
private String scriptData;
@Override
public int getId() {
return this.id;
}
public SceneType getSceneType() {
return type;
}
public String getScriptData() {
return scriptData;
}
@Override
public void onLoad() {
}
@Getter(onMethod = @__(@Override))
private int id;
@SerializedName("type")
private SceneType sceneType;
private String scriptData;
}
@@ -2,10 +2,11 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "TowerFloorExcelConfigData.json")
@Getter
public class TowerFloorData extends GameResource {
private int floorId;
private int floorIndex;
private int levelGroupId;
@@ -17,33 +18,4 @@ public class TowerFloorData extends GameResource {
public int getId() {
return this.floorId;
}
@Override
public void onLoad() {
super.onLoad();
}
public int getFloorId() {
return floorId;
}
public int getFloorIndex() {
return floorIndex;
}
public int getLevelGroupId() {
return levelGroupId;
}
public int getOverrideMonsterLevel() {
return overrideMonsterLevel;
}
public int getTeamNum() {
return teamNum;
}
public int getFloorLevelConfigId() {
return floorLevelConfigId;
}
}
@@ -1,6 +1,7 @@
package emu.grasscutter.database;
import java.util.List;
import java.util.stream.Stream;
import com.mongodb.client.result.DeleteResult;
@@ -154,6 +155,11 @@ public final class DatabaseHelper {
DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete();
}
public static <T> Stream<T> getByGameClass(Class<T> classType) {
return DatabaseManager.getGameDatastore().find(classType).stream();
}
@Deprecated(forRemoval = true)
public static List<Player> getAllPlayers() {
return DatabaseManager.getGameDatastore().find(Player.class).stream().toList();
}
@@ -58,6 +58,7 @@ import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo;
import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@@ -89,7 +90,7 @@ public class Avatar {
private float currentEnergy;
@Transient @Getter private final Int2ObjectMap<GameItem> equips;
@Transient private final Int2FloatOpenHashMap fightProp;
@Transient @Getter private final Int2FloatOpenHashMap fightProperties;
@Transient @Getter private final Int2FloatOpenHashMap fightPropOverrides;
@Transient @Getter private Set<String> extraAbilityEmbryos;
@@ -115,7 +116,7 @@ public class Avatar {
@Deprecated // Do not use. Morhpia only!
public Avatar() {
this.equips = new Int2ObjectOpenHashMap<>();
this.fightProp = new Int2FloatOpenHashMap();
this.fightProperties = new Int2FloatOpenHashMap();
this.fightPropOverrides = new Int2FloatOpenHashMap();
this.extraAbilityEmbryos = new HashSet<>();
this.fetters = new ArrayList<>(); // TODO Move to avatar
@@ -276,10 +277,6 @@ public class Avatar {
}
}
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;
}
public void setFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), value);
}
@@ -399,9 +396,9 @@ public class Avatar {
public void recalcStats(boolean forceSendAbilityChange) {
// Setup
AvatarData data = this.getAvatarData();
AvatarPromoteData promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel());
Int2IntOpenHashMap setMap = new Int2IntOpenHashMap();
var data = this.getAvatarData();
var promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel());
var setMap = new Int2IntOpenHashMap();
// Extra ability embryos
Set<String> prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos();
@@ -579,21 +576,11 @@ public class Avatar {
// Add any skill strings from this constellation
// Set % stats
this.setFightProperty(
FightProperty.FIGHT_PROP_MAX_HP,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_ATTACK,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_DEFENSE,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
);
FightProperty.forEachCompoundProperty(c -> this.setFightProperty(c.getResult(),
this.getFightProperty(c.getFlat()) + (this.getFightProperty(c.getBase()) * (1f + this.getFightProperty(c.getPercent())))));
// Reapply all overrides
this.fightProp.putAll(this.fightPropOverrides);
this.fightProperties.putAll(this.fightPropOverrides);
// Set current hp
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
@@ -1,124 +0,0 @@
package emu.grasscutter.game.avatar;
import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public enum AvatarStat {
FIGHT_PROP_NONE(0),
FIGHT_PROP_BASE_HP(1),
FIGHT_PROP_HP(2),
FIGHT_PROP_HP_PERCENT(3),
FIGHT_PROP_BASE_ATTACK(4),
FIGHT_PROP_ATTACK(5),
FIGHT_PROP_ATTACK_PERCENT(6),
FIGHT_PROP_BASE_DEFENSE(7),
FIGHT_PROP_DEFENSE(8),
FIGHT_PROP_DEFENSE_PERCENT(9),
FIGHT_PROP_BASE_SPEED(10),
FIGHT_PROP_SPEED_PERCENT(11),
FIGHT_PROP_HP_MP_PERCENT(12),
FIGHT_PROP_ATTACK_MP_PERCENT(13),
FIGHT_PROP_CRITICAL(20),
FIGHT_PROP_ANTI_CRITICAL(21),
FIGHT_PROP_CRITICAL_HURT(22),
FIGHT_PROP_CHARGE_EFFICIENCY(23),
FIGHT_PROP_ADD_HURT(24),
FIGHT_PROP_SUB_HURT(25),
FIGHT_PROP_HEAL_ADD(26),
FIGHT_PROP_HEALED_ADD(27),
FIGHT_PROP_ELEMENT_MASTERY(28),
FIGHT_PROP_PHYSICAL_SUB_HURT(29),
FIGHT_PROP_PHYSICAL_ADD_HURT(30),
FIGHT_PROP_DEFENCE_IGNORE_RATIO(31),
FIGHT_PROP_DEFENCE_IGNORE_DELTA(32),
FIGHT_PROP_FIRE_ADD_HURT(40),
FIGHT_PROP_ELEC_ADD_HURT(41),
FIGHT_PROP_WATER_ADD_HURT(42),
FIGHT_PROP_GRASS_ADD_HURT(43),
FIGHT_PROP_WIND_ADD_HURT(44),
FIGHT_PROP_ROCK_ADD_HURT(45),
FIGHT_PROP_ICE_ADD_HURT(46),
FIGHT_PROP_HIT_HEAD_ADD_HURT(47),
FIGHT_PROP_FIRE_SUB_HURT(50),
FIGHT_PROP_ELEC_SUB_HURT(51),
FIGHT_PROP_WATER_SUB_HURT(52),
FIGHT_PROP_GRASS_SUB_HURT(53),
FIGHT_PROP_WIND_SUB_HURT(54),
FIGHT_PROP_ROCK_SUB_HURT(55),
FIGHT_PROP_ICE_SUB_HURT(56),
FIGHT_PROP_EFFECT_HIT(60),
FIGHT_PROP_EFFECT_RESIST(61),
FIGHT_PROP_FREEZE_RESIST(62),
FIGHT_PROP_TORPOR_RESIST(63),
FIGHT_PROP_DIZZY_RESIST(64),
FIGHT_PROP_FREEZE_SHORTEN(65),
FIGHT_PROP_TORPOR_SHORTEN(66),
FIGHT_PROP_DIZZY_SHORTEN(67),
FIGHT_PROP_MAX_FIRE_ENERGY(70),
FIGHT_PROP_MAX_ELEC_ENERGY(71),
FIGHT_PROP_MAX_WATER_ENERGY(72),
FIGHT_PROP_MAX_GRASS_ENERGY(73),
FIGHT_PROP_MAX_WIND_ENERGY(74),
FIGHT_PROP_MAX_ICE_ENERGY(75),
FIGHT_PROP_MAX_ROCK_ENERGY(76),
FIGHT_PROP_SKILL_CD_MINUS_RATIO(80),
FIGHT_PROP_SHIELD_COST_MINUS_RATIO(81),
FIGHT_PROP_CUR_FIRE_ENERGY(1000),
FIGHT_PROP_CUR_ELEC_ENERGY(1001),
FIGHT_PROP_CUR_WATER_ENERGY(1002),
FIGHT_PROP_CUR_GRASS_ENERGY(1003),
FIGHT_PROP_CUR_WIND_ENERGY(1004),
FIGHT_PROP_CUR_ICE_ENERGY(1005),
FIGHT_PROP_CUR_ROCK_ENERGY(1006),
FIGHT_PROP_CUR_HP(1010),
FIGHT_PROP_MAX_HP(2000),
FIGHT_PROP_CUR_ATTACK(2001),
FIGHT_PROP_CUR_DEFENSE(2002),
FIGHT_PROP_CUR_SPEED(2003),
FIGHT_PROP_NONEXTRA_ATTACK(3000),
FIGHT_PROP_NONEXTRA_DEFENSE(3001),
FIGHT_PROP_NONEXTRA_CRITICAL(3002),
FIGHT_PROP_NONEXTRA_ANTI_CRITICAL(3003),
FIGHT_PROP_NONEXTRA_CRITICAL_HURT(3004),
FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY(3005),
FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY(3006),
FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT(3007),
FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT(3008),
FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT(3009),
FIGHT_PROP_NONEXTRA_WATER_ADD_HURT(3010),
FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT(3011),
FIGHT_PROP_NONEXTRA_WIND_ADD_HURT(3012),
FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT(3013),
FIGHT_PROP_NONEXTRA_ICE_ADD_HURT(3014),
FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT(3015),
FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT(3016),
FIGHT_PROP_NONEXTRA_WATER_SUB_HURT(3017),
FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT(3018),
FIGHT_PROP_NONEXTRA_WIND_SUB_HURT(3019),
FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT(3020),
FIGHT_PROP_NONEXTRA_ICE_SUB_HURT(3021),
FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO(3022),
FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO(3023),
FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT(3024);
private final int id;
private static final Int2ObjectMap<AvatarStat> map = new Int2ObjectOpenHashMap<>();
static {
Stream.of(values()).forEach(e -> map.put(e.getId(), e));
}
private AvatarStat(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AvatarStat getStatById(int value) {
return map.getOrDefault(value, FIGHT_PROP_NONE);
}
}
@@ -21,7 +21,6 @@ import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
@@ -38,20 +37,25 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.Getter;
import lombok.val;
public class EntityAvatar extends GameEntity {
private final Avatar avatar;
@Getter private final Avatar avatar;
private PlayerDieType killedType;
private int killedBy;
@Getter private PlayerDieType killedType;
@Getter private int killedBy;
public EntityAvatar(Avatar avatar) {
this(null, avatar);
}
public EntityAvatar(Scene scene, Avatar avatar) {
super(scene);
this.avatar = avatar;
this.avatar.setCurrentEnergy();
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
if (scene != null)
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
GameItem weapon = this.getAvatar().getWeapon();
if (weapon != null) {
@@ -59,47 +63,20 @@ public class EntityAvatar extends GameEntity {
}
}
public EntityAvatar(Avatar avatar) {
super(null);
this.avatar = avatar;
this.avatar.setCurrentEnergy();
}
public Player getPlayer() {
return avatar.getPlayer();
}
public Player getPlayer() {return this.avatar.getPlayer();}
@Override
public Position getPosition() {
return getPlayer().getPosition();
}
public Position getPosition() {return getPlayer().getPosition();}
@Override
public Position getRotation() {
return getPlayer().getRotation();
}
public Avatar getAvatar() {
return avatar;
}
public int getKilledBy() {
return killedBy;
}
public PlayerDieType getKilledType() {
return killedType;
}
public Position getRotation() {return getPlayer().getRotation();}
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return getAvatar().getFightProperties();
}
@Override public Int2FloatMap getFightProperties() {return getAvatar().getFightProperties();}
public int getWeaponEntityId() {
if (getAvatar().getWeapon() != null) {
@@ -145,11 +122,8 @@ public class EntityAvatar extends GameEntity {
public void clearEnergy(ChangeEnergyReason reason) {
// Fight props.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
// Get max energy.
float maxEnergy = this.avatar.getFightProperty(maxEnergyProp);
val curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
float curEnergy = this.getFightProperty(curEnergyProp);
// Set energy to zero.
this.avatar.setCurrentEnergy(curEnergyProp, 0);
@@ -158,7 +132,7 @@ public class EntityAvatar extends GameEntity {
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason));
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason));
}
}
@@ -167,18 +141,16 @@ public class EntityAvatar extends GameEntity {
}
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
// Get current and maximum energy for this avatar.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
val elementType = this.getAvatar().getSkillDepot().getElementType();
val curEnergyProp = elementType.getCurEnergyProp();
val maxEnergyProp = elementType.getMaxEnergyProp();
float curEnergy = this.getFightProperty(curEnergyProp);
float maxEnergy = this.getFightProperty(maxEnergyProp);
// Get energy recharge.
float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
// Scale amount by energy recharge, if the amount is not flat.
if (!isFlat) {
amount *= energyRecharge;
amount *= this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
}
// Determine the new energy value.
@@ -3,11 +3,23 @@ package emu.grasscutter.game.entity;
import emu.grasscutter.data.binout.ConfigGadget;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.utils.Position;
import lombok.Getter;
public abstract class EntityBaseGadget extends GameEntity {
@Getter(onMethod = @__(@Override))
protected final Position position;
@Getter(onMethod = @__(@Override))
protected final Position rotation;
public EntityBaseGadget(Scene scene) {
this(scene, null, null);
}
public EntityBaseGadget(Scene scene, Position position, Position rotation) {
super(scene);
this.position = position != null ? position.clone() : new Position();
this.rotation = rotation != null ? rotation.clone() : new Position();
}
public abstract int getGadgetId();
@@ -19,30 +19,28 @@ import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityClientGadget extends EntityBaseGadget {
private final Player owner;
@Getter private final Player owner;
private final Position pos;
private final Position rot;
@Getter(onMethod = @__(@Override))
private int gadgetId;
private int configId;
private int campId;
private int campType;
private int ownerEntityId;
private int targetEntityId;
private boolean asyncLoad;
@Getter private int campId;
@Getter private int campType;
@Getter private int ownerEntityId;
@Getter private int targetEntityId;
@Getter private boolean asyncLoad;
private int originalOwnerEntityId;
@Getter private int originalOwnerEntityId;
public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) {
super(scene);
super(scene, new Position(notify.getInitPos()), new Position(notify.getInitEulerAngles()));
this.owner = player;
this.id = notify.getEntityId();
this.pos = new Position(notify.getInitPos());
this.rot = new Position(notify.getInitEulerAngles());
this.configId = notify.getConfigId();
this.gadgetId = notify.getConfigId();
this.campId = notify.getCampId();
this.campType = notify.getCampType();
this.ownerEntityId = notify.getPropOwnerEntityId();
@@ -58,61 +56,12 @@ public class EntityClientGadget extends EntityBaseGadget {
}
}
@Override
public int getGadgetId() {
return configId;
}
public Player getOwner() {
return owner;
}
public int getCampId() {
return campId;
}
public int getCampType() {
return campType;
}
public int getOwnerEntityId() {
return ownerEntityId;
}
public int getTargetEntityId() {
return targetEntityId;
}
public boolean isAsyncLoad() {
return this.asyncLoad;
}
public int getOriginalOwnerEntityId() {
return this.originalOwnerEntityId;
}
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
// TODO Auto-generated method stub
return null;
}
@Override
public Position getPosition() {
// TODO Auto-generated method stub
return this.pos;
}
@Override
public Position getRotation() {
// TODO Auto-generated method stub
return this.rot;
}
@Override public Int2FloatMap getFightProperties() {return null;}
@Override
public SceneEntityInfo toProto() {
@@ -6,8 +6,6 @@ import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.entity.gadget.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
@@ -29,77 +27,47 @@ import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.Optional;
import javax.annotation.Nullable;
@ToString(callSuper = true)
public class EntityGadget extends EntityBaseGadget {
private final GadgetData data;
private final Position pos;
private final Position rot;
@Getter private final GadgetData gadgetData;
@Getter(onMethod = @__(@Override)) @Setter
private int gadgetId;
private int state;
private int pointType;
private GadgetContent content;
private Int2FloatOpenHashMap fightProp;
private SceneGadget metaGadget;
@Getter @Setter private int state;
@Getter @Setter private int pointType;
@Getter private GadgetContent content;
@Getter(onMethod = @__(@Override), lazy = true)
private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
@Getter @Setter private SceneGadget metaGadget;
@Nullable @Getter
private ConfigGadget configGadget;
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
super(scene);
this.data = GameData.getGadgetDataMap().get(gadgetId);
if (data!=null && data.getJsonName()!=null) {
this.configGadget = GameData.getGadgetConfigData().get(data.getJsonName());
}
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId;
this.pos = pos.clone();
this.rot = rot != null ? rot.clone() : new Position();
fillFightProps(configGadget);
public EntityGadget(Scene scene, int gadgetId, Position pos) {
this(scene, gadgetId, pos, null, null);
}
public EntityGadget(Scene scene, int gadgetId, Position pos) {
this(scene, gadgetId, pos, new Position());
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
this(scene, gadgetId, pos, rot, null);
}
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
this(scene, gadgetId, pos, rot);
this.content = content;
}
public GadgetData getGadgetData() {
return data;
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
public int getGadgetId() {
return gadgetId;
}
public void setGadgetId(int gadgetId) {
super(scene, pos, rot);
this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
this.configGadget = Optional.ofNullable(this.gadgetData).map(GadgetData::getJsonName).map(GameData.getGadgetConfigData()::get).orElse(null);
this.id = this.getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
this.content = content;
fillFightProps(configGadget);
}
public void updateState(int state) {
@@ -108,39 +76,18 @@ public class EntityGadget extends EntityBaseGadget {
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
}
public int getPointType() {
return pointType;
}
public void setPointType(int pointType) {
this.pointType = pointType;
}
public GadgetContent getContent() {
return content;
}
@Deprecated // Dont use!
@Deprecated(forRemoval = true) // Dont use!
public void setContent(GadgetContent content) {
this.content = this.content == null ? content : this.content;
}
public SceneGadget getMetaGadget() {
return metaGadget;
}
public void setMetaGadget(SceneGadget metaGadget) {
this.metaGadget = metaGadget;
}
// TODO refactor
public void buildContent() {
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
if (this.getContent() != null || this.getGadgetData() == null || this.getGadgetData().getType() == null) {
return;
}
EntityType type = getGadgetData().getType();
GadgetContent content = switch (type) {
this.content = switch (this.getGadgetData().getType()) {
case GatherPoint -> new GadgetGatherPoint(this);
case GatherObject -> new GadgetGatherObject(this);
case Worktop -> new GadgetWorktop(this);
@@ -149,14 +96,6 @@ public class EntityGadget extends EntityBaseGadget {
case Gadget -> new GadgetObject(this);
default -> null;
};
this.content = content;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
if (this.fightProp == null) this.fightProp = new Int2FloatOpenHashMap();
return this.fightProp;
}
@Override
@@ -216,7 +155,7 @@ public class EntityGadget extends EntityBaseGadget {
entityInfo.addPropList(pair);
// We do not use the getter to null check because the getter will create a fight prop map if it is null
if (this.fightProp != null) {
if (this.fightProperties != null) {
addAllFightPropsToEntityInfo(entityInfo);
}
@@ -25,57 +25,33 @@ import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityItem extends EntityBaseGadget {
private final Position pos;
private final Position rot;
private final GameItem item;
private final long guid;
private final boolean share;
@Getter private final GameItem item;
@Getter private final long guid;
@Getter private final boolean share;
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = true;
this(scene, player, itemData, pos, count, true);
}
// In official game, some drop items are shared to all players, and some other items are independent to all players
// For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
// but if you broke regional mine, when someone picked up the drop then it disappeared
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
super(scene);
super(scene, pos, null);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = share;
}
@Override
public int getId() {
return this.id;
}
private GameItem getItem() {
return this.item;
}
public ItemData getItemData() {
return this.getItem().getItemData();
}
public long getGuid() {
return guid;
}
public int getCount() {
return this.getItem().getCount();
}
@@ -85,24 +61,7 @@ public class EntityItem extends EntityBaseGadget {
return this.getItemData().getGadgetId();
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
public boolean isShare() {
return share;
}
@Override public Int2FloatMap getFightProperties() {return null;}
@Override
public void onInteract(Player player, GadgetInteractReq interactReq) {
@@ -1,14 +1,12 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.Grasscutter;
import java.util.Optional;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.excels.EnvAnimalGatherConfigData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.MonsterCurveData;
import emu.grasscutter.data.excels.MonsterData;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityIdType;
@@ -16,13 +14,11 @@ import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.VisionTypeOuterClass;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.MonsterBornTypeOuterClass.MonsterBornType;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
@@ -35,31 +31,32 @@ import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.Getter;
import lombok.Setter;
public class EntityMonster extends GameEntity {
private final MonsterData monsterData;
private final Int2FloatOpenHashMap fightProp;
@Getter private final MonsterData monsterData;
@Getter(onMethod = @__(@Override))
private final Int2FloatOpenHashMap fightProperties;
private final Position pos;
private final Position rot;
private final Position bornPos;
private final int level;
@Getter(onMethod = @__(@Override))
private final Position position;
@Getter(onMethod = @__(@Override))
private final Position rotation;
@Getter private final Position bornPos;
@Getter private final int level;
private int weaponEntityId;
private int poseId;
@Getter @Setter
private int aiId=-1;
@Getter @Setter private int poseId;
@Getter @Setter private int aiId = -1;
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
super(scene);
this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
this.monsterData = monsterData;
this.fightProp = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position();
this.fightProperties = new Int2FloatOpenHashMap();
this.position = new Position(pos);
this.rotation = new Position();
this.bornPos = getPosition().clone();
this.level = level;
@@ -71,59 +68,19 @@ public class EntityMonster extends GameEntity {
this.recalcStats();
}
@Override
public int getId() {
return this.id;
}
public MonsterData getMonsterData() {
return monsterData;
}
public int getMonsterWeaponId() {
return getMonsterData().getWeaponId();
return this.getMonsterData().getWeaponId();
}
private int getMonsterId() {
return this.getMonsterData().getId();
}
public int getLevel() {
return level;
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
public Position getBornPos() {
return bornPos;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;
}
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
public int getPoseId() {
return poseId;
}
public void setPoseId(int poseId) {
this.poseId = poseId;
}
@Override
public void onInteract(Player player, GadgetInteractReq interactReq) {
EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
@@ -163,27 +120,24 @@ public class EntityMonster extends GameEntity {
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
var scene = this.getScene();
var challenge = Optional.ofNullable(scene.getChallenge());
var scriptManager = scene.getScriptManager();
Optional.ofNullable(this.getSpawnEntry()).ifPresent(scene.getDeadSpawnedEntities()::add);
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
// first set the challenge data
if (getScene().getChallenge() != null) {
getScene().getChallenge().onMonsterDeath(this);
}
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
if (getScene().getScriptManager().getScriptMonsterSpawnService() != null) {
getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this);
}
challenge.ifPresent(c -> c.onMonsterDeath(this));
if (scriptManager.isInit() && this.getGroupId() > 0) {
Optional.ofNullable(scriptManager.getScriptMonsterSpawnService()).ifPresent(s -> s.onMonsterDead(this));
// prevent spawn monster after success
if (getScene().getChallenge() != null && getScene().getChallenge().inProgress()) {
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}else if (getScene().getChallenge() == null) {
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}
if (challenge.map(c -> c.inProgress()).orElse(true))
scriptManager.callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}
// Battle Pass trigger
getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
scene.getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
}
public void recalcStats() {
@@ -197,18 +151,7 @@ public class EntityMonster extends GameEntity {
this.getFightProperties().clear();
// Base stats
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp());
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack());
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense());
this.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, data.getPhysicalSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_FIRE_SUB_HURT, .1f);
this.setFightProperty(FightProperty.FIGHT_PROP_ELEC_SUB_HURT, data.getElecSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_WATER_SUB_HURT, data.getWaterSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_GRASS_SUB_HURT, data.getGrassSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_WIND_SUB_HURT, data.getWindSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_ROCK_SUB_HURT, .1f);
this.setFightProperty(FightProperty.FIGHT_PROP_ICE_SUB_HURT, data.getIceSubHurt());
MonsterData.definedFightProperties.forEach(prop -> this.setFightProperty(prop, data.getFightProperty(prop)));
// Level curve
MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel());
@@ -220,18 +163,8 @@ public class EntityMonster extends GameEntity {
}
// Set % stats
this.setFightProperty(
FightProperty.FIGHT_PROP_MAX_HP,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_ATTACK,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_DEFENSE,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
);
FightProperty.forEachCompoundProperty(c -> this.setFightProperty(c.getResult(),
this.getFightProperty(c.getFlat()) + (this.getFightProperty(c.getBase()) * (1f + this.getFightProperty(c.getPercent())))));
// Set current hp
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
@@ -239,14 +172,14 @@ public class EntityMonster extends GameEntity {
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
var authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
.setBornPos(this.getBornPos().toProto())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
var entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
.setMotionInfo(this.getMotionInfo())
@@ -257,13 +190,12 @@ public class EntityMonster extends GameEntity {
this.addAllFightPropsToEntityInfo(entityInfo);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
.build();
entityInfo.addPropList(pair);
entityInfo.addPropList(PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
.build());
SceneMonsterInfo.Builder monsterInfo = SceneMonsterInfo.newBuilder()
var monsterInfo = SceneMonsterInfo.newBuilder()
.setMonsterId(getMonsterId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
@@ -5,14 +5,16 @@ import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.scripts.data.SceneNPC;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityNPC extends GameEntity{
@Getter(onMethod = @__(@Override))
private final Position position;
@Getter(onMethod = @__(@Override))
private final Position rotation;
private final SceneNPC metaNpc;
private final int suiteId;
@Getter private final int suiteId;
public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) {
super(scene);
@@ -27,24 +29,7 @@ public class EntityNPC extends GameEntity{
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public Position getRotation() {
return rotation;
}
public int getSuiteId() {
return suiteId;
}
@Override public Int2FloatMap getFightProperties() {return null;}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
@@ -5,7 +5,7 @@ import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass;
import emu.grasscutter.scripts.data.SceneRegion;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
import java.util.Set;
@@ -57,20 +57,11 @@ public class EntityRegion extends GameEntity{
}
public boolean entityLeave() {return this.entityLeave;}
public void resetEntityLeave() {this.entityLeave = false;}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
@Override public Int2FloatMap getFightProperties() {return null;}
@Override
public Position getPosition() {
return position;
}
@Override public Position getPosition() {return position;}
@Override
public Position getRotation() {
return null;
}
@Override public Position getRotation() {return null;}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
@@ -12,7 +12,6 @@ import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncState
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
@@ -36,10 +35,8 @@ import java.util.List;
public class EntityVehicle extends EntityBaseGadget {
@Getter private final Player owner;
private final Int2FloatMap fightProp;
private final Position pos;
private final Position rot;
@Getter(onMethod = @__(@Override))
private final Int2FloatMap fightProperties;
@Getter private final int pointId;
@Getter private final int gadgetId;
@@ -49,12 +46,10 @@ public class EntityVehicle extends EntityBaseGadget {
@Nullable @Getter private ConfigGadget configGadget;
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
super(scene);
super(scene, pos, rot);
this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.fightProp = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position(rot);
this.fightProperties = new Int2FloatOpenHashMap();
this.gadgetId = gadgetId;
this.pointId = pointId;
this.curStamina = 240; // might be in configGadget.GCALKECLLLP.JBAKBEFIMBN.ANBMPHPOALP
@@ -74,19 +69,6 @@ public class EntityVehicle extends EntityBaseGadget {
this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0);
}
@Override
public Int2FloatMap getFightProperties() {
return fightProp;
}
@Override
public Position getPosition() { return this.pos; }
@Override
public Position getRotation() {
return this.rot;
}
@Override
public SceneEntityInfo toProto() {
@@ -33,7 +33,7 @@ public abstract class GameEntity {
@Getter @Setter private int configId;
@Getter @Setter private int groupId;
private MotionState moveState;
@Getter @Setter private MotionState motionState;
@Getter @Setter private int lastMoveSceneTimeMs;
@Getter @Setter private int lastMoveReliableSeq;
@@ -45,7 +45,7 @@ public abstract class GameEntity {
public GameEntity(Scene scene) {
this.scene = scene;
this.moveState = MotionState.MOTION_STATE_NONE;
this.motionState = MotionState.MOTION_STATE_NONE;
}
public int getEntityType() {
@@ -84,14 +84,6 @@ public abstract class GameEntity {
public abstract Position getRotation();
public MotionState getMotionState() {
return moveState;
}
public void setMotionState(MotionState moveState) {
this.moveState = moveState;
}
public void setFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), value);
}
@@ -20,12 +20,12 @@ import javax.annotation.Nullable;
public class EntityPlatform extends EntityBaseGadget {
@Getter
private final Player owner;
@Getter(onMethod = @__(@Override))
private final int gadgetId;
@Getter
private final EntityClientGadget gadget;
private final Int2FloatMap fightProp;
private final Position pos;
private final Position rot;
@Getter(onMethod = @__(@Override))
private final Int2FloatMap fightProperties;
@Nullable
@Getter
private ConfigGadget configGadget;
@@ -39,13 +39,11 @@ public class EntityPlatform extends EntityBaseGadget {
private boolean isActive;
public EntityPlatform(EntityClientGadget gadget, Scene scene, Player player, int gadgetId, Position pos, Position rot, MovingPlatformTypeOuterClass.MovingPlatformType movingPlatformType) {
super(scene);
super(scene, pos, rot);
this.gadget = gadget;
this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.fightProp = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position(rot);
this.fightProperties = new Int2FloatOpenHashMap();
this.movingPlatformType = movingPlatformType;
this.gadgetId = gadgetId;
GadgetData data = GameData.getGadgetDataMap().get(gadgetId);
@@ -56,26 +54,6 @@ public class EntityPlatform extends EntityBaseGadget {
fillFightProps(configGadget);
}
@Override
public int getGadgetId() {
return gadgetId;
}
@Override
public Int2FloatMap getFightProperties() {
return fightProp;
}
@Override
public Position getPosition() {
return pos;
}
@Override
public Position getRotation() {
return rot;
}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
var platform = PlatformInfoOuterClass.PlatformInfo.newBuilder()
@@ -2,6 +2,7 @@ package emu.grasscutter.game.gacha;
import static emu.grasscutter.config.Configuration.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.GachaInfoOuterClass.GachaInfo;
@@ -10,65 +11,123 @@ import emu.grasscutter.utils.Utils;
import lombok.Getter;
public class GachaBanner {
@Getter private int gachaType;
@Getter private int scheduleId;
@Getter private int gachaType = -1;
@Getter int scheduleId = -1;
@Getter int sortId = -1;
@Getter private String prefabPath;
private String previewPrefabPath;
@Getter private String previewPrefabPath;
@Getter private String titlePath;
private int costItemId = 0;
private int costItemAmount = 1;
private int costItemId10 = 0;
private int costItemAmount10 = 10;
@Getter private int beginTime;
@Getter private int endTime;
@Getter private int sortId;
@Getter private int beginTime = 0;
@Getter private int endTime = 1924992000;
@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
private int[] rateUpItems4 = {};
private int[] rateUpItems5 = {};
@Getter private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
@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 int[] rateUpItems4 = {};
@Getter private int[] rateUpItems5 = {};
// This now handles default values for the fields below
@Getter private BannerType bannerType = BannerType.STANDARD;
// Kinda wanna deprecate these but they're in people's configs
private int[] rateUpItems1 = {};
private int[] rateUpItems2 = {};
private int eventChance = -1;
private int costItem = 0;
// 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;
public String getPreviewPrefabPath() {
if (this.previewPrefabPath != null && !this.previewPrefabPath.isEmpty())
return this.previewPrefabPath;
return "UI_Tab_" + this.prefabPath;
// Deprecated fields that were tolerated in early May 2022 but have apparently still being circulating in new custom configs
// For now, throw up big scary errors on load telling people that they will be banned outright in a future version
@Deprecated private int[] rateUpItems1 = {};
@Deprecated private int[] rateUpItems2 = {};
@Deprecated private int eventChance = -1;
@Deprecated private int costItem = 0;
@Deprecated private int softPity = -1;
@Deprecated private int hardPity = -1;
@Deprecated private int minItemType = -1;
@Deprecated private int maxItemType = -1;
@Getter private boolean deprecated = false;
@Getter private boolean disabled = false;
private void warnDeprecated(String name, String replacement) {
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() {
// Handle deprecated configs
if (eventChance != -1)
warnDeprecated("eventChance", "eventChance4 & eventChance5");
if (costItem != 0)
warnDeprecated("costItem", "costItemId");
if (softPity != -1)
warnDeprecated("softPity", "weights5");
if (hardPity != -1)
warnDeprecated("hardPity", "weights5");
if (minItemType != -1)
warnDeprecated("minItemType", "fallbackItems[4,5]Pool[1,2]");
if (maxItemType != -1)
warnDeprecated("maxItemType", "fallbackItems[4,5]Pool[1,2]");
if (rateUpItems1.length > 0)
warnDeprecated("rateUpItems1", "rateUpItems5");
if (rateUpItems2.length > 0)
warnDeprecated("rateUpItems2", "rateUpItems4");
// Handle default values
if (this.previewPrefabPath != null && this.previewPrefabPath.equals("UI_Tab_" + this.prefabPath))
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_\".");
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) {
return switch (numRolls) {
case 10 -> new ItemParamData((costItemId10 > 0) ? costItemId10 : getCostItem(), costItemAmount10);
default -> new ItemParamData(getCostItem(), costItemAmount * numRolls);
case 10 -> new ItemParamData(costItemId10, costItemAmount10);
default -> new ItemParamData(costItemId, costItemAmount * numRolls);
};
}
@Deprecated
public int getCostItem() {
return (costItem > 0) ? costItem : costItemId;
}
public int[] getRateUpItems4() {
return (rateUpItems2.length > 0) ? rateUpItems2 : rateUpItems4;
}
public int[] getRateUpItems5() {
return (rateUpItems1.length > 0) ? rateUpItems1 : rateUpItems5;
return costItemId;
}
public boolean hasEpitomized() {
@@ -92,7 +151,7 @@ public class GachaBanner {
public int getEventChance(int rarity) {
return switch (rarity) {
case 4 -> eventChance4;
default -> (eventChance > -1) ? eventChance : eventChance5;
default -> eventChance5;
};
}
@@ -110,8 +169,6 @@ public class GachaBanner {
+ "/gacha/details?s=" + sessionKey + "&scheduleId=" + scheduleId;
// Grasscutter.getLogger().info("record = " + record);
ItemParamData costItem1 = this.getCost(1);
ItemParamData costItem10 = this.getCost(10);
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
int leftGachaTimes = switch (gachaTimesLimit) {
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
@@ -122,10 +179,10 @@ public class GachaBanner {
.setScheduleId(this.getScheduleId())
.setBeginTime(this.getBeginTime())
.setEndTime(this.getEndTime())
.setCostItemId(costItem1.getId())
.setCostItemNum(costItem1.getCount())
.setTenCostItemId(costItem10.getId())
.setTenCostItemNum(costItem10.getCount())
.setCostItemId(this.costItemId)
.setCostItemNum(this.costItemAmount)
.setTenCostItemId(this.costItemId10)
.setTenCostItemNum(this.costItemAmount10)
.setGachaPrefabPath(this.getPrefabPath())
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
.setGachaProbUrl(details)
@@ -174,6 +231,31 @@ public class GachaBanner {
}
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 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) {
super(server);
@@ -69,11 +67,24 @@ public class GachaSystem extends BaseGameSystem {
public synchronized void load() {
getGachaBanners().clear();
int autoScheduleId = 1000;
int autoSortId = 9000;
try {
List<GachaBanner> banners = DataLoader.loadList("Banners.json", GachaBanner.class);
List<GachaBanner> banners = DataLoader.loadTableToList("Banners", GachaBanner.class);
if (banners.size() > 0) {
for (GachaBanner banner : banners) {
getGachaBanners().put(banner.getScheduleId(), banner);
banner.onLoad();
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.");
} else {
@@ -155,7 +166,7 @@ public class GachaSystem extends BaseGameSystem {
private synchronized int doFallbackRarePull(int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
if (fallback1.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 {
return getRandom(fallback2);
}
@@ -4,37 +4,43 @@ import dev.morphia.annotations.Entity;
@Entity
public class PlayerGachaInfo {
private PlayerGachaBannerInfo standardBanner;
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;
}
private PlayerGachaBannerInfo standardBanner;
private PlayerGachaBannerInfo beginnerBanner;
private PlayerGachaBannerInfo eventCharacterBanner;
private PlayerGachaBannerInfo eventWeaponBanner;
public PlayerGachaBannerInfo getEventCharacterBanner() {
return eventCharacterBanner;
}
public PlayerGachaBannerInfo getEventWeaponBanner() {
return eventWeaponBanner;
}
public PlayerGachaInfo() {
this.standardBanner = new PlayerGachaBannerInfo();
this.eventCharacterBanner = new PlayerGachaBannerInfo();
this.eventWeaponBanner = new PlayerGachaBannerInfo();
}
public PlayerGachaBannerInfo getBannerInfo(GachaBanner banner) {
switch (banner.getBannerType()) {
case EVENT:
return this.eventCharacterBanner;
case WEAPON:
return this.eventWeaponBanner;
case STANDARD:
default:
return this.standardBanner;
}
}
public PlayerGachaBannerInfo getStandardBanner() {
if (this.standardBanner == null) this.standardBanner = new PlayerGachaBannerInfo();
return this.standardBanner;
}
public PlayerGachaBannerInfo getBeginnerBanner() {
if (this.beginnerBanner == null) this.beginnerBanner = new PlayerGachaBannerInfo();
return this.beginnerBanner;
}
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();
};
}
}
@@ -181,6 +181,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
if (data.isUseOnGain()) {
var params = new UseItemParams(this.player, data.getUseTarget());
params.usedItemId = data.getId();
this.player.getServer().getInventorySystem().useItemDirect(data, params);
return null;
}
@@ -36,6 +36,11 @@ public class DeforestationManager extends BasePlayerManager {
ColliderTypeToWoodItemID.put(10,101310);
ColliderTypeToWoodItemID.put(11,101311);
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) {
@@ -1,6 +1,6 @@
package emu.grasscutter.game.player;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -15,11 +15,8 @@ import emu.grasscutter.server.packet.send.PacketServerBuffChangeNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.val;
@Getter(AccessLevel.PRIVATE)
public class PlayerBuffManager extends BasePlayerManager {
private int nextBuffUid;
@@ -29,7 +26,7 @@ public class PlayerBuffManager extends BasePlayerManager {
public PlayerBuffManager(Player player) {
super(player);
this.buffs = new Int2ObjectOpenHashMap<>();
this.pendingBuffs = new LinkedList<>();
this.pendingBuffs = new ArrayList<>();
}
/**
@@ -46,7 +43,7 @@ public class PlayerBuffManager extends BasePlayerManager {
* @return True if a buff with this group id exists
*/
public synchronized boolean hasBuff(int groupId) {
return this.getBuffs().containsKey(groupId);
return this.buffs.containsKey(groupId);
}
/**
@@ -55,11 +52,11 @@ public class PlayerBuffManager extends BasePlayerManager {
public synchronized void clearBuffs() {
// Remove from player
getPlayer().sendPacket(
new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, getBuffs().values())
new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, this.buffs.values())
);
// Clear
getBuffs().clear();
this.buffs.clear();
}
/**
@@ -133,13 +130,11 @@ public class PlayerBuffManager extends BasePlayerManager {
}
// Clear previous buff if it exists
if (this.hasBuff(buffData.getGroupId())) {
this.removeBuff(buffData.getGroupId());
}
this.removeBuff(buffData.getGroupId());
// Create and store buff
PlayerBuff buff = new PlayerBuff(getNextBuffUid(), buffData, duration);
getBuffs().put(buff.getGroupId(), buff);
this.buffs.put(buff.getGroupId(), buff);
// Packet
getPlayer().sendPacket(new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_ADD_SERVER_BUFF, buff));
@@ -153,7 +148,7 @@ public class PlayerBuffManager extends BasePlayerManager {
* @return True if a buff was remove
*/
public synchronized boolean removeBuff(int buffGroupId) {
PlayerBuff buff = this.getBuffs().get(buffGroupId);
PlayerBuff buff = this.buffs.remove(buffGroupId);
if (buff != null) {
getPlayer().sendPacket(
@@ -167,28 +162,24 @@ public class PlayerBuffManager extends BasePlayerManager {
public synchronized void onTick() {
// Skip if no buffs
if (getBuffs().size() == 0) return;
if (this.buffs.isEmpty()) return;
long currentTime = System.currentTimeMillis();
// Add to pending buffs to remove if buff has expired
for (PlayerBuff buff : getBuffs().values()) {
if (currentTime > buff.getEndTime()) {
this.getPendingBuffs().add(buff);
}
}
this.buffs.values().removeIf(buff -> {
if (currentTime <= buff.getEndTime())
return false;
this.pendingBuffs.add(buff);
return true;
});
if (this.getPendingBuffs().size() > 0) {
if (this.pendingBuffs.size() > 0) {
// Send packet
getPlayer().sendPacket(
new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, this.pendingBuffs)
);
// Remove buff from player buff map
for (PlayerBuff buff : this.getPendingBuffs()) {
getBuffs().remove(buff.getGroupId());
}
this.getPendingBuffs().clear();
this.pendingBuffs.clear();
}
}
@@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import static java.util.Map.entry;
@@ -12,133 +13,134 @@ import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
public enum FightProperty {
FIGHT_PROP_NONE(0),
FIGHT_PROP_BASE_HP(1),
FIGHT_PROP_HP(2),
FIGHT_PROP_HP_PERCENT(3),
FIGHT_PROP_BASE_ATTACK(4),
FIGHT_PROP_ATTACK(5),
FIGHT_PROP_ATTACK_PERCENT(6),
FIGHT_PROP_BASE_DEFENSE(7),
FIGHT_PROP_DEFENSE(8),
FIGHT_PROP_DEFENSE_PERCENT(9),
FIGHT_PROP_BASE_SPEED(10),
FIGHT_PROP_SPEED_PERCENT(11),
FIGHT_PROP_HP_MP_PERCENT(12),
FIGHT_PROP_ATTACK_MP_PERCENT(13),
FIGHT_PROP_CRITICAL(20),
FIGHT_PROP_ANTI_CRITICAL(21),
FIGHT_PROP_CRITICAL_HURT(22),
FIGHT_PROP_CHARGE_EFFICIENCY(23),
FIGHT_PROP_ADD_HURT(24),
FIGHT_PROP_SUB_HURT(25),
FIGHT_PROP_HEAL_ADD(26),
FIGHT_PROP_HEALED_ADD(27),
FIGHT_PROP_ELEMENT_MASTERY(28),
FIGHT_PROP_PHYSICAL_SUB_HURT(29),
FIGHT_PROP_PHYSICAL_ADD_HURT(30),
FIGHT_PROP_DEFENCE_IGNORE_RATIO(31),
FIGHT_PROP_DEFENCE_IGNORE_DELTA(32),
FIGHT_PROP_FIRE_ADD_HURT(40),
FIGHT_PROP_ELEC_ADD_HURT(41),
FIGHT_PROP_WATER_ADD_HURT(42),
FIGHT_PROP_GRASS_ADD_HURT(43),
FIGHT_PROP_WIND_ADD_HURT(44),
FIGHT_PROP_ROCK_ADD_HURT(45),
FIGHT_PROP_ICE_ADD_HURT(46),
FIGHT_PROP_HIT_HEAD_ADD_HURT(47),
FIGHT_PROP_FIRE_SUB_HURT(50),
FIGHT_PROP_ELEC_SUB_HURT(51),
FIGHT_PROP_WATER_SUB_HURT(52),
FIGHT_PROP_GRASS_SUB_HURT(53),
FIGHT_PROP_WIND_SUB_HURT(54),
FIGHT_PROP_ROCK_SUB_HURT(55),
FIGHT_PROP_ICE_SUB_HURT(56),
FIGHT_PROP_EFFECT_HIT(60),
FIGHT_PROP_EFFECT_RESIST(61),
FIGHT_PROP_FREEZE_RESIST(62),
FIGHT_PROP_TORPOR_RESIST(63),
FIGHT_PROP_DIZZY_RESIST(64),
FIGHT_PROP_FREEZE_SHORTEN(65),
FIGHT_PROP_TORPOR_SHORTEN(66),
FIGHT_PROP_DIZZY_SHORTEN(67),
FIGHT_PROP_MAX_FIRE_ENERGY(70),
FIGHT_PROP_MAX_ELEC_ENERGY(71),
FIGHT_PROP_MAX_WATER_ENERGY(72),
FIGHT_PROP_MAX_GRASS_ENERGY(73),
FIGHT_PROP_MAX_WIND_ENERGY(74),
FIGHT_PROP_MAX_ICE_ENERGY(75),
FIGHT_PROP_MAX_ROCK_ENERGY(76),
FIGHT_PROP_SKILL_CD_MINUS_RATIO(80),
FIGHT_PROP_SHIELD_COST_MINUS_RATIO(81),
FIGHT_PROP_CUR_FIRE_ENERGY(1000),
FIGHT_PROP_CUR_ELEC_ENERGY(1001),
FIGHT_PROP_CUR_WATER_ENERGY(1002),
FIGHT_PROP_CUR_GRASS_ENERGY(1003),
FIGHT_PROP_CUR_WIND_ENERGY(1004),
FIGHT_PROP_CUR_ICE_ENERGY(1005),
FIGHT_PROP_CUR_ROCK_ENERGY(1006),
FIGHT_PROP_CUR_HP(1010),
FIGHT_PROP_MAX_HP(2000),
FIGHT_PROP_CUR_ATTACK(2001),
FIGHT_PROP_CUR_DEFENSE(2002),
FIGHT_PROP_CUR_SPEED(2003),
FIGHT_PROP_NONEXTRA_ATTACK(3000),
FIGHT_PROP_NONEXTRA_DEFENSE(3001),
FIGHT_PROP_NONEXTRA_CRITICAL(3002),
FIGHT_PROP_NONEXTRA_ANTI_CRITICAL(3003),
FIGHT_PROP_NONEXTRA_CRITICAL_HURT(3004),
FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY(3005),
FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY(3006),
FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT(3007),
FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT(3008),
FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT(3009),
FIGHT_PROP_NONEXTRA_WATER_ADD_HURT(3010),
FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT(3011),
FIGHT_PROP_NONEXTRA_WIND_ADD_HURT(3012),
FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT(3013),
FIGHT_PROP_NONEXTRA_ICE_ADD_HURT(3014),
FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT(3015),
FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT(3016),
FIGHT_PROP_NONEXTRA_WATER_SUB_HURT(3017),
FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT(3018),
FIGHT_PROP_NONEXTRA_WIND_SUB_HURT(3019),
FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT(3020),
FIGHT_PROP_NONEXTRA_ICE_SUB_HURT(3021),
FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO(3022),
FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO(3023),
FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT(3024);
private final int id;
private static final Int2ObjectMap<FightProperty> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, FightProperty> stringMap = new HashMap<>();
public static final int[] fightProps = new int[] {1, 4, 7, 20, 21, 22, 23, 26, 27, 28, 29, 30, 40, 41, 42, 43, 44, 45, 46, 50, 51, 52, 53, 54, 55, 56, 2000, 2001, 2002, 2003, 1010};
static {
Stream.of(values()).forEach(e -> {
map.put(e.getId(), e);
stringMap.put(e.name(), e);
});
}
private FightProperty(int id) {
this.id = id;
}
FIGHT_PROP_NONE(0),
FIGHT_PROP_BASE_HP(1),
FIGHT_PROP_HP(2),
FIGHT_PROP_HP_PERCENT(3),
FIGHT_PROP_BASE_ATTACK(4),
FIGHT_PROP_ATTACK(5),
FIGHT_PROP_ATTACK_PERCENT(6),
FIGHT_PROP_BASE_DEFENSE(7),
FIGHT_PROP_DEFENSE(8),
FIGHT_PROP_DEFENSE_PERCENT(9),
FIGHT_PROP_BASE_SPEED(10),
FIGHT_PROP_SPEED_PERCENT(11),
FIGHT_PROP_HP_MP_PERCENT(12),
FIGHT_PROP_ATTACK_MP_PERCENT(13),
FIGHT_PROP_CRITICAL(20),
FIGHT_PROP_ANTI_CRITICAL(21),
FIGHT_PROP_CRITICAL_HURT(22),
FIGHT_PROP_CHARGE_EFFICIENCY(23),
FIGHT_PROP_ADD_HURT(24),
FIGHT_PROP_SUB_HURT(25),
FIGHT_PROP_HEAL_ADD(26),
FIGHT_PROP_HEALED_ADD(27),
FIGHT_PROP_ELEMENT_MASTERY(28),
FIGHT_PROP_PHYSICAL_SUB_HURT(29),
FIGHT_PROP_PHYSICAL_ADD_HURT(30),
FIGHT_PROP_DEFENCE_IGNORE_RATIO(31),
FIGHT_PROP_DEFENCE_IGNORE_DELTA(32),
FIGHT_PROP_FIRE_ADD_HURT(40),
FIGHT_PROP_ELEC_ADD_HURT(41),
FIGHT_PROP_WATER_ADD_HURT(42),
FIGHT_PROP_GRASS_ADD_HURT(43),
FIGHT_PROP_WIND_ADD_HURT(44),
FIGHT_PROP_ROCK_ADD_HURT(45),
FIGHT_PROP_ICE_ADD_HURT(46),
FIGHT_PROP_HIT_HEAD_ADD_HURT(47),
FIGHT_PROP_FIRE_SUB_HURT(50),
FIGHT_PROP_ELEC_SUB_HURT(51),
FIGHT_PROP_WATER_SUB_HURT(52),
FIGHT_PROP_GRASS_SUB_HURT(53),
FIGHT_PROP_WIND_SUB_HURT(54),
FIGHT_PROP_ROCK_SUB_HURT(55),
FIGHT_PROP_ICE_SUB_HURT(56),
FIGHT_PROP_EFFECT_HIT(60),
FIGHT_PROP_EFFECT_RESIST(61),
FIGHT_PROP_FREEZE_RESIST(62),
FIGHT_PROP_TORPOR_RESIST(63),
FIGHT_PROP_DIZZY_RESIST(64),
FIGHT_PROP_FREEZE_SHORTEN(65),
FIGHT_PROP_TORPOR_SHORTEN(66),
FIGHT_PROP_DIZZY_SHORTEN(67),
FIGHT_PROP_MAX_FIRE_ENERGY(70),
FIGHT_PROP_MAX_ELEC_ENERGY(71),
FIGHT_PROP_MAX_WATER_ENERGY(72),
FIGHT_PROP_MAX_GRASS_ENERGY(73),
FIGHT_PROP_MAX_WIND_ENERGY(74),
FIGHT_PROP_MAX_ICE_ENERGY(75),
FIGHT_PROP_MAX_ROCK_ENERGY(76),
FIGHT_PROP_SKILL_CD_MINUS_RATIO(80),
FIGHT_PROP_SHIELD_COST_MINUS_RATIO(81),
FIGHT_PROP_CUR_FIRE_ENERGY(1000),
FIGHT_PROP_CUR_ELEC_ENERGY(1001),
FIGHT_PROP_CUR_WATER_ENERGY(1002),
FIGHT_PROP_CUR_GRASS_ENERGY(1003),
FIGHT_PROP_CUR_WIND_ENERGY(1004),
FIGHT_PROP_CUR_ICE_ENERGY(1005),
FIGHT_PROP_CUR_ROCK_ENERGY(1006),
FIGHT_PROP_CUR_HP(1010),
FIGHT_PROP_MAX_HP(2000),
FIGHT_PROP_CUR_ATTACK(2001),
FIGHT_PROP_CUR_DEFENSE(2002),
FIGHT_PROP_CUR_SPEED(2003),
FIGHT_PROP_NONEXTRA_ATTACK(3000),
FIGHT_PROP_NONEXTRA_DEFENSE(3001),
FIGHT_PROP_NONEXTRA_CRITICAL(3002),
FIGHT_PROP_NONEXTRA_ANTI_CRITICAL(3003),
FIGHT_PROP_NONEXTRA_CRITICAL_HURT(3004),
FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY(3005),
FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY(3006),
FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT(3007),
FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT(3008),
FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT(3009),
FIGHT_PROP_NONEXTRA_WATER_ADD_HURT(3010),
FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT(3011),
FIGHT_PROP_NONEXTRA_WIND_ADD_HURT(3012),
FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT(3013),
FIGHT_PROP_NONEXTRA_ICE_ADD_HURT(3014),
FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT(3015),
FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT(3016),
FIGHT_PROP_NONEXTRA_WATER_SUB_HURT(3017),
FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT(3018),
FIGHT_PROP_NONEXTRA_WIND_SUB_HURT(3019),
FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT(3020),
FIGHT_PROP_NONEXTRA_ICE_SUB_HURT(3021),
FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO(3022),
FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO(3023),
FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT(3024);
public int getId() {
return id;
}
public static FightProperty getPropById(int value) {
return map.getOrDefault(value, FIGHT_PROP_NONE);
}
public static FightProperty getPropByName(String name) {
return stringMap.getOrDefault(name, FIGHT_PROP_NONE);
}
private final int id;
private static final Int2ObjectMap<FightProperty> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, FightProperty> stringMap = new HashMap<>();
public static final int[] fightProps = new int[] {1, 4, 7, 20, 21, 22, 23, 26, 27, 28, 29, 30, 40, 41, 42, 43, 44, 45, 46, 50, 51, 52, 53, 54, 55, 56, 2000, 2001, 2002, 2003, 1010};
static {
Stream.of(values()).forEach(e -> {
map.put(e.getId(), e);
stringMap.put(e.name(), e);
});
}
private FightProperty(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static FightProperty getPropById(int value) {
return map.getOrDefault(value, FIGHT_PROP_NONE);
}
public static FightProperty getPropByName(String name) {
return stringMap.getOrDefault(name, FIGHT_PROP_NONE);
}
public static FightProperty getPropByShortName(String name) {
return shortNameMap.getOrDefault(name, FIGHT_PROP_NONE);
@@ -151,28 +153,28 @@ public enum FightProperty {
// This was originally for relic properties so some names might not be applicable for e.g. setstats
private static final Map<String, FightProperty> shortNameMap = Map.ofEntries(
// Normal relic stats
entry("hp", FIGHT_PROP_HP),
entry("atk", FIGHT_PROP_ATTACK),
entry("def", FIGHT_PROP_DEFENSE),
entry("hp%", FIGHT_PROP_HP_PERCENT),
entry("atk%", FIGHT_PROP_ATTACK_PERCENT),
entry("def%", FIGHT_PROP_DEFENSE_PERCENT),
entry("em", FIGHT_PROP_ELEMENT_MASTERY),
entry("er", FIGHT_PROP_CHARGE_EFFICIENCY),
entry("hb", FIGHT_PROP_HEAL_ADD),
entry("hp", FIGHT_PROP_HP),
entry("atk", FIGHT_PROP_ATTACK),
entry("def", FIGHT_PROP_DEFENSE),
entry("hp%", FIGHT_PROP_HP_PERCENT),
entry("atk%", FIGHT_PROP_ATTACK_PERCENT),
entry("def%", FIGHT_PROP_DEFENSE_PERCENT),
entry("em", FIGHT_PROP_ELEMENT_MASTERY),
entry("er", FIGHT_PROP_CHARGE_EFFICIENCY),
entry("hb", FIGHT_PROP_HEAL_ADD),
entry("heal", FIGHT_PROP_HEAL_ADD),
entry("cd", FIGHT_PROP_CRITICAL_HURT),
entry("cdmg", FIGHT_PROP_CRITICAL_HURT),
entry("cr", FIGHT_PROP_CRITICAL),
entry("crate", FIGHT_PROP_CRITICAL),
entry("phys%", FIGHT_PROP_PHYSICAL_ADD_HURT),
entry("dendro%", FIGHT_PROP_GRASS_ADD_HURT),
entry("geo%", FIGHT_PROP_ROCK_ADD_HURT),
entry("anemo%", FIGHT_PROP_WIND_ADD_HURT),
entry("hydro%", FIGHT_PROP_WATER_ADD_HURT),
entry("cryo%", FIGHT_PROP_ICE_ADD_HURT),
entry("electro%", FIGHT_PROP_ELEC_ADD_HURT),
entry("pyro%", FIGHT_PROP_FIRE_ADD_HURT),
entry("cd", FIGHT_PROP_CRITICAL_HURT),
entry("cdmg", FIGHT_PROP_CRITICAL_HURT),
entry("cr", FIGHT_PROP_CRITICAL),
entry("crate", FIGHT_PROP_CRITICAL),
entry("phys%", FIGHT_PROP_PHYSICAL_ADD_HURT),
entry("dendro%", FIGHT_PROP_GRASS_ADD_HURT),
entry("geo%", FIGHT_PROP_ROCK_ADD_HURT),
entry("anemo%", FIGHT_PROP_WIND_ADD_HURT),
entry("hydro%", FIGHT_PROP_WATER_ADD_HURT),
entry("cryo%", FIGHT_PROP_ICE_ADD_HURT),
entry("electro%", FIGHT_PROP_ELEC_ADD_HURT),
entry("pyro%", FIGHT_PROP_FIRE_ADD_HURT),
// Other stats
entry("maxhp", FIGHT_PROP_MAX_HP),
entry("dmg", FIGHT_PROP_ADD_HURT), // This seems to get reset after attacks
@@ -189,14 +191,43 @@ public enum FightProperty {
entry("reshydro", FIGHT_PROP_WATER_SUB_HURT),
entry("respyro", FIGHT_PROP_FIRE_SUB_HURT),
entry("resphys", FIGHT_PROP_PHYSICAL_SUB_HURT)
);
);
private static final List<FightProperty> flatProps = Arrays.asList(
FIGHT_PROP_BASE_HP, FIGHT_PROP_HP, FIGHT_PROP_BASE_ATTACK, FIGHT_PROP_ATTACK, FIGHT_PROP_BASE_DEFENSE,
FIGHT_PROP_DEFENSE, FIGHT_PROP_HEALED_ADD, FIGHT_PROP_CUR_FIRE_ENERGY, FIGHT_PROP_CUR_ELEC_ENERGY,
FIGHT_PROP_CUR_WATER_ENERGY, FIGHT_PROP_CUR_GRASS_ENERGY, FIGHT_PROP_CUR_WIND_ENERGY, FIGHT_PROP_CUR_ICE_ENERGY,
FIGHT_PROP_CUR_ROCK_ENERGY, FIGHT_PROP_CUR_HP, FIGHT_PROP_MAX_HP, FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_CUR_DEFENSE);
FIGHT_PROP_CUR_ROCK_ENERGY, FIGHT_PROP_CUR_HP, FIGHT_PROP_MAX_HP, FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_CUR_DEFENSE);
@Getter
public static class CompoundProperty {
private FightProperty result;
private FightProperty base;
private FightProperty percent;
private FightProperty flat;
public CompoundProperty(FightProperty result, FightProperty base, FightProperty percent, FightProperty flat) {
this.result = result;
this.base = base;
this.percent = percent;
this.flat = flat;
}
}
private static Map<FightProperty, CompoundProperty> compoundProperties = Map.ofEntries(
entry(FIGHT_PROP_MAX_HP, new CompoundProperty(FIGHT_PROP_MAX_HP, FIGHT_PROP_BASE_HP, FIGHT_PROP_HP_PERCENT, FIGHT_PROP_HP)),
entry(FIGHT_PROP_CUR_ATTACK, new CompoundProperty(FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_BASE_ATTACK, FIGHT_PROP_ATTACK_PERCENT, FIGHT_PROP_ATTACK)),
entry(FIGHT_PROP_CUR_DEFENSE, new CompoundProperty(FIGHT_PROP_CUR_DEFENSE, FIGHT_PROP_BASE_DEFENSE, FIGHT_PROP_DEFENSE_PERCENT, FIGHT_PROP_DEFENSE))
);
public static CompoundProperty getCompoundProperty(FightProperty result) {
return compoundProperties.get(result);
}
public static void forEachCompoundProperty(Consumer<CompoundProperty> consumer) {
compoundProperties.values().forEach(consumer);
}
public static boolean isPercentage(FightProperty prop) {
return !flatProps.contains(prop);
}
@@ -24,7 +24,7 @@ public class ItemUseAction {
case ITEM_USE_GAIN_AVATAR -> new ItemUseGainAvatar(useParam);
case ITEM_USE_GAIN_COSTUME -> new ItemUseGainCostume(useParam); // TODO - real success/fail
case ITEM_USE_GAIN_FLYCLOAK -> new ItemUseGainFlycloak(useParam); // TODO - real success/fail
case ITEM_USE_GAIN_NAME_CARD -> new ItemUseGainNameCard(useParam); // TODO
case ITEM_USE_GAIN_NAME_CARD -> new ItemUseGainNameCard(useParam);
case ITEM_USE_CHEST_SELECT_ITEM -> new ItemUseChestSelectItem(useParam);
case ITEM_USE_ADD_SELECT_ITEM -> new ItemUseAddSelectItem(useParam);
case ITEM_USE_GRANT_SELECT_REWARD -> new ItemUseGrantSelectReward(useParam);
@@ -1,19 +1,18 @@
package emu.grasscutter.game.props.ItemUseAction;
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
public ItemUseOp getItemUseOp() {
return ItemUseOp.ITEM_USE_ADD_EXP;
}
public ItemUseAddExp(String[] useParam) {
try {
this.exp = Integer.parseInt(useParam[0]);
} catch (NumberFormatException ignored) {}
super(useParam);
}
public int getExp() {
return this.i;
}
}
@@ -14,7 +14,7 @@ public class ItemUseAddItem extends ItemUseInt {
super(useParam);
try {
this.count = Integer.parseInt(useParam[1]);
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
}
@Override
@@ -1,19 +1,14 @@
package emu.grasscutter.game.props.ItemUseAction;
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
public ItemUseOp getItemUseOp() {
return ItemUseOp.ITEM_USE_ADD_RELIQUARY_EXP;
}
public ItemUseAddReliquaryExp(String[] useParam) {
try {
this.exp = Integer.parseInt(useParam[0]);
} catch (NumberFormatException ignored) {}
super(useParam);
}
}
@@ -14,7 +14,7 @@ public class ItemUseAddServerBuff extends ItemUseInt {
super(useParam);
try {
this.duration = Integer.parseInt(useParam[1]);
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
}
@Override
@@ -1,19 +1,14 @@
package emu.grasscutter.game.props.ItemUseAction;
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
public ItemUseOp getItemUseOp() {
return ItemUseOp.ITEM_USE_ADD_WEAPON_EXP;
}
public ItemUseAddWeaponExp(String[] useParam) {
try {
this.exp = Integer.parseInt(useParam[0]);
} catch (NumberFormatException ignored) {}
super(useParam);
}
}
@@ -15,10 +15,10 @@ public class ItemUseCombineItem extends ItemUseInt {
super(useParam);
try {
this.resultId = Integer.parseInt(useParam[1]);
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
try {
this.resultCount = Integer.parseInt(useParam[2]);
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
}
@Override
@@ -19,10 +19,10 @@ public class ItemUseGainAvatar extends ItemUseInt {
super(useParam);
try {
this.level = Integer.parseInt(useParam[1]);
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
try {
this.constellation = Integer.parseInt(useParam[2]);
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
}
@Override
@@ -13,6 +13,7 @@ public class ItemUseGainNameCard extends ItemUseAction {
@Override
public boolean useItem(UseItemParams params) {
return false; // TODO: work out if this is actually used and how to get the namecard id
params.player.addNameCard(params.usedItemId);
return true;
}
}
@@ -8,6 +8,6 @@ public abstract class ItemUseInt extends ItemUseAction {
public ItemUseInt(String[] useParam) {
try {
this.i = Integer.parseInt(useParam[0]);
} catch (NumberFormatException ignored) {}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
}
}
@@ -12,6 +12,7 @@ public class UseItemParams {
public int count = 1;
public int optionId = 0;
public boolean isEnterMpDungeonTeam = false;
public int usedItemId = 0;
public UseItemParams(Player player, ItemUseTarget itemUseTarget, Avatar targetAvatar, int count, int optionId, boolean isEnterMpDungeonTeam) {
this.player = player;
@@ -54,7 +54,7 @@ public class QuestSystem extends BaseGameSystem {
return;
}
map.put(opcode.value().getValue(), handlerClass.newInstance());
map.put(opcode.value().getValue(), handlerClass.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
@@ -743,6 +743,7 @@ public class InventorySystem extends BaseGameSystem {
if (itemData == null) return null;
var params = new UseItemParams(player, itemData.getUseTarget(), target, count, optionId, isEnterMpDungeonTeam);
params.usedItemId = item.getItemId();
if (useItemDirect(itemData, params)) {
player.getInventory().removeItem(item, count);
var actions = itemData.getItemUseActions();
@@ -274,15 +274,8 @@ public class Scene {
}
public void showOtherEntities(Player player) {
List<GameEntity> entities = new LinkedList<>();
GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity();
for (GameEntity entity : this.getEntities().values()) {
if (entity == currentEntity) {
continue;
}
entities.add(entity);
}
List<GameEntity> entities = this.getEntities().values().stream().filter(entity -> entity != currentEntity).toList();
player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET));
}
@@ -422,8 +415,8 @@ public class Scene {
}
// Todo
List<GameEntity> toAdd = new LinkedList<>();
List<GameEntity> toRemove = new LinkedList<>();
List<GameEntity> toAdd = new ArrayList<>();
List<GameEntity> toRemove = new ArrayList<>();
var spawnedEntities = this.getSpawnedEntities();
for (SpawnDataEntry entry : visible) {
// If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it
@@ -27,7 +27,8 @@ public class PacketOpcodesUtils {
PacketOpcodes.PingRsp,
PacketOpcodes.WorldPlayerRTTNotify,
PacketOpcodes.UnionCmdNotify,
PacketOpcodes.QueryPathReq
PacketOpcodes.QueryPathReq,
PacketOpcodes.QueryPathRsp
);
static {
@@ -181,7 +181,7 @@ public final class PluginManager {
// Add the plugin to the list of loaded plugins.
this.plugins.put(identifier.name, plugin);
// Create a collection for the plugin's listeners.
this.listeners.put(plugin, new LinkedList<>());
this.listeners.put(plugin, new ArrayList<>());
// Call the plugin's onLoad method.
try {
@@ -242,18 +242,14 @@ public final class PluginManager {
* @param priority The priority to call for.
*/
private void checkAndFilter(Event event, HandlerPriority priority) {
// Create a collection of listeners.
List<EventHandler<? extends Event>> listeners = new LinkedList<>();
// Add all listeners from every plugin.
this.listeners.values().forEach(listeners::addAll);
listeners.stream()
this.listeners.values().stream()
.flatMap(Collection::stream)
// Filter the listeners by priority.
.filter(handler -> handler.handles().isInstance(event))
.filter(handler -> handler.getPriority() == priority)
// Invoke the event.
.toList().forEach(handler -> this.invokeHandler(event, handler));
.forEach(handler -> this.invokeHandler(event, handler));
}
/**
@@ -11,8 +11,9 @@ import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.http.HttpServer;
import emu.grasscutter.server.http.Router;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* Hooks into the {@link GameServer} class, adding convenient ways to do certain things.
@@ -60,8 +61,17 @@ public final class ServerHook {
* Gets all online players.
* @return Players connected to the server.
*/
@Deprecated(forRemoval = true)
public List<Player> getOnlinePlayers() {
return new LinkedList<>(this.gameServer.getPlayers().values());
return new ArrayList<>(this.gameServer.getPlayers().values());
}
/**
* Gets all online players.
* @return Players connected to the server.
*/
public Stream<Player> getOnlinePlayersStream() {
return this.gameServer.getPlayers().values().stream();
}
/**

Some files were not shown because too many files have changed in this diff Show More