diff --git a/README.md b/README.md index bffa93df1..67cd2e116 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ - Get Java 17: https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html - Get [MongoDB Community Server](https://www.mongodb.com/try/download/community) -- Get game version REL3.7 (3.7 client can be found here if you don't have it): https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/3.7.0.md +- Get game version REL4.0.x (4.0.x client can be found here if you don't have it): https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/4.0.0.md - Download the [latest Cultivation version](https://github.com/Grasscutters/Cultivation/releases/latest). Use the `.msi` installer. - After opening Culivation (as admin), press the download button in the upper right corner. @@ -46,25 +46,49 @@ Grasscutter uses Gradle to handle dependencies & building. **Requirements:** -- [Java SE Development Kits - 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) or higher +- [Java Development Kit 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) or higher - [Git](https://git-scm.com/downloads) +- [NodeJS](https://nodejs.org/en/download) (Optional, for building the handbook) -##### Windows +##### Clone ```shell git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git cd Grasscutter -.\gradlew.bat # Setting up environments -.\gradlew jar # Compile ``` -##### Linux (GNU) +##### Compile + +**Note**: Handbook generation may fail on some systems. To disable the handbook generation, append `-PskipHandbook=1` to the `gradlew jar` command. + +Windows: + +```shell +.\gradlew.bat # Setting up environments +.\gradlew jar +``` + +Linux (GNU): ```bash -git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git -cd Grasscutter chmod +x gradlew -./gradlew jar # Compile +./gradlew jar +``` + +##### Compiling the Handbook (Manually) + +With Gradle: + +```shell +./gradlew generateHandbook +``` + +With NPM: + +```shell +cd src/handbook +npm install +npm run build ``` You can find the output jar in the root of the project folder. diff --git a/docs/README_HE.md b/docs/README_HE.md index e98882d10..1fd3f9b66 100644 --- a/docs/README_HE.md +++ b/docs/README_HE.md @@ -3,7 +3,7 @@
-[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) | [VI](README_vi-VN.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) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.md) **תשומת לב בבקשה:** אנחנו מקבלים עזרה בפיתוח התוכנה. לפני שאתם תורמים לפרויקט בבקשה תקראו את [תנאי השימוש](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). diff --git a/docs/README_NL.md b/docs/README_NL.md index b620a149c..e746f4acc 100644 --- a/docs/README_NL.md +++ b/docs/README_NL.md @@ -3,7 +3,7 @@ -[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) | [VI](README_vi-VN.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) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.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. diff --git a/docs/README_es-ES.md b/docs/README_es-ES.md index 661ee86ff..f436d2309 100644 --- a/docs/README_es-ES.md +++ b/docs/README_es-ES.md @@ -3,7 +3,7 @@ -[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) | [VI](README_vi-VN.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) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.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). diff --git a/docs/README_fil-PH.md b/docs/README_fil-PH.md index 57e2198fa..77d3da4df 100644 --- a/docs/README_fil-PH.md +++ b/docs/README_fil-PH.md @@ -3,7 +3,7 @@ -[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) | [VI](README_vi-VN.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) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.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). diff --git a/docs/README_fr-FR.md b/docs/README_fr-FR.md index 2644f9011..93890f8ff 100644 --- a/docs/README_fr-FR.md +++ b/docs/README_fr-FR.md @@ -3,7 +3,7 @@ -[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) | [VI](README_vi-VN.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) | [VI](README_vi-VN.md) | [हिंदी](README_hn-IN.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). @@ -71,4 +71,4 @@ Vous pouvez trouver le jar de sortie dans la racine du dossier du projet. ### Dépanage -Pour une liste des problèmes communs et leur solution et pour demander de l'aide, veuillez rejoindre [notre serveur Discord](https://discord.gg/T5vZU6UyeG) (en anglais) et dirigez vous vers le salon de support. \ No newline at end of file +Pour une liste des problèmes communs et leur solution et pour demander de l'aide, veuillez rejoindre [notre serveur Discord](https://discord.gg/T5vZU6UyeG) (en anglais) et dirigez vous vers le salon de support. diff --git a/docs/README_hn-IN.md b/docs/README_hn-IN.md new file mode 100644 index 000000000..0d33308b9 --- /dev/null +++ b/docs/README_hn-IN.md @@ -0,0 +1,78 @@ +![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) +.HomePlantFieldStatus CAKDDMKAIMD = 7;
- * @return The enum numeric value on the wire for cAKDDMKAIMD.
+ * .HomePlantFieldStatus status = 7;
+ * @return The enum numeric value on the wire for status.
*/
- int getCAKDDMKAIMDValue();
+ int getStatusValue();
/**
- * .HomePlantFieldStatus CAKDDMKAIMD = 7;
- * @return The cAKDDMKAIMD.
+ * .HomePlantFieldStatus status = 7;
+ * @return The status.
*/
- emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getCAKDDMKAIMD();
+ emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getStatus();
/**
- * uint32 JHFNDBIHLNB = 8;
- * @return The jHFNDBIHLNB.
+ * uint32 seed_id = 8;
+ * @return The seedId.
*/
- int getJHFNDBIHLNB();
+ int getSeedId();
/**
* fixed32 end_time = 14;
@@ -59,10 +59,10 @@ public final class HomePlantSubFieldDataOuterClass {
int getEndTime();
/**
- * uint32 KHFGOPCOAGM = 3;
- * @return The kHFGOPCOAGM.
+ * uint32 gather_point_type = 3;
+ * @return The gatherPointType.
*/
- int getKHFGOPCOAGM();
+ int getGatherPointType();
}
/**
* @@ -82,7 +82,7 @@ public final class HomePlantSubFieldDataOuterClass { } private HomePlantSubFieldData() { entityIdList_ = emptyIntList(); - cAKDDMKAIMD_ = 0; + status_ = 0; } @java.lang.Override @@ -118,7 +118,7 @@ public final class HomePlantSubFieldDataOuterClass { break; case 24: { - kHFGOPCOAGM_ = input.readUInt32(); + gatherPointType_ = input.readUInt32(); break; } case 48: { @@ -145,12 +145,12 @@ public final class HomePlantSubFieldDataOuterClass { case 56: { int rawValue = input.readEnum(); - cAKDDMKAIMD_ = rawValue; + status_ = rawValue; break; } case 64: { - jHFNDBIHLNB_ = input.readUInt32(); + seedId_ = input.readUInt32(); break; } case 117: { @@ -221,34 +221,34 @@ public final class HomePlantSubFieldDataOuterClass { } private int entityIdListMemoizedSerializedSize = -1; - public static final int CAKDDMKAIMD_FIELD_NUMBER = 7; - private int cAKDDMKAIMD_; + public static final int STATUS_FIELD_NUMBER = 7; + private int status_; /** - *.HomePlantFieldStatus CAKDDMKAIMD = 7;
- * @return The enum numeric value on the wire for cAKDDMKAIMD. + *.HomePlantFieldStatus status = 7;
+ * @return The enum numeric value on the wire for status. */ - @java.lang.Override public int getCAKDDMKAIMDValue() { - return cAKDDMKAIMD_; + @java.lang.Override public int getStatusValue() { + return status_; } /** - *.HomePlantFieldStatus CAKDDMKAIMD = 7;
- * @return The cAKDDMKAIMD. + *.HomePlantFieldStatus status = 7;
+ * @return The status. */ - @java.lang.Override public emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getCAKDDMKAIMD() { + @java.lang.Override public emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getStatus() { @SuppressWarnings("deprecation") - emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus result = emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.valueOf(cAKDDMKAIMD_); + emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus result = emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.valueOf(status_); return result == null ? emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.UNRECOGNIZED : result; } - public static final int JHFNDBIHLNB_FIELD_NUMBER = 8; - private int jHFNDBIHLNB_; + public static final int SEED_ID_FIELD_NUMBER = 8; + private int seedId_; /** - *uint32 JHFNDBIHLNB = 8;
- * @return The jHFNDBIHLNB. + *uint32 seed_id = 8;
+ * @return The seedId. */ @java.lang.Override - public int getJHFNDBIHLNB() { - return jHFNDBIHLNB_; + public int getSeedId() { + return seedId_; } public static final int END_TIME_FIELD_NUMBER = 14; @@ -262,15 +262,15 @@ public final class HomePlantSubFieldDataOuterClass { return endTime_; } - public static final int KHFGOPCOAGM_FIELD_NUMBER = 3; - private int kHFGOPCOAGM_; + public static final int GATHER_POINT_TYPE_FIELD_NUMBER = 3; + private int gatherPointType_; /** - *uint32 KHFGOPCOAGM = 3;
- * @return The kHFGOPCOAGM. + *uint32 gather_point_type = 3;
+ * @return The gatherPointType. */ @java.lang.Override - public int getKHFGOPCOAGM() { - return kHFGOPCOAGM_; + public int getGatherPointType() { + return gatherPointType_; } private byte memoizedIsInitialized = -1; @@ -288,8 +288,8 @@ public final class HomePlantSubFieldDataOuterClass { public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { getSerializedSize(); - if (kHFGOPCOAGM_ != 0) { - output.writeUInt32(3, kHFGOPCOAGM_); + if (gatherPointType_ != 0) { + output.writeUInt32(3, gatherPointType_); } if (getEntityIdListList().size() > 0) { output.writeUInt32NoTag(50); @@ -298,11 +298,11 @@ public final class HomePlantSubFieldDataOuterClass { for (int i = 0; i < entityIdList_.size(); i++) { output.writeUInt32NoTag(entityIdList_.getInt(i)); } - if (cAKDDMKAIMD_ != emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.HOME_FIELD_STATUE_NONE.getNumber()) { - output.writeEnum(7, cAKDDMKAIMD_); + if (status_ != emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.HOME_FIELD_STATUE_NONE.getNumber()) { + output.writeEnum(7, status_); } - if (jHFNDBIHLNB_ != 0) { - output.writeUInt32(8, jHFNDBIHLNB_); + if (seedId_ != 0) { + output.writeUInt32(8, seedId_); } if (endTime_ != 0) { output.writeFixed32(14, endTime_); @@ -316,9 +316,9 @@ public final class HomePlantSubFieldDataOuterClass { if (size != -1) return size; size = 0; - if (kHFGOPCOAGM_ != 0) { + if (gatherPointType_ != 0) { size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, kHFGOPCOAGM_); + .computeUInt32Size(3, gatherPointType_); } { int dataSize = 0; @@ -334,13 +334,13 @@ public final class HomePlantSubFieldDataOuterClass { } entityIdListMemoizedSerializedSize = dataSize; } - if (cAKDDMKAIMD_ != emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.HOME_FIELD_STATUE_NONE.getNumber()) { + if (status_ != emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.HOME_FIELD_STATUE_NONE.getNumber()) { size += com.google.protobuf.CodedOutputStream - .computeEnumSize(7, cAKDDMKAIMD_); + .computeEnumSize(7, status_); } - if (jHFNDBIHLNB_ != 0) { + if (seedId_ != 0) { size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(8, jHFNDBIHLNB_); + .computeUInt32Size(8, seedId_); } if (endTime_ != 0) { size += com.google.protobuf.CodedOutputStream @@ -363,13 +363,13 @@ public final class HomePlantSubFieldDataOuterClass { if (!getEntityIdListList() .equals(other.getEntityIdListList())) return false; - if (cAKDDMKAIMD_ != other.cAKDDMKAIMD_) return false; - if (getJHFNDBIHLNB() - != other.getJHFNDBIHLNB()) return false; + if (status_ != other.status_) return false; + if (getSeedId() + != other.getSeedId()) return false; if (getEndTime() != other.getEndTime()) return false; - if (getKHFGOPCOAGM() - != other.getKHFGOPCOAGM()) return false; + if (getGatherPointType() + != other.getGatherPointType()) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -385,14 +385,14 @@ public final class HomePlantSubFieldDataOuterClass { hash = (37 * hash) + ENTITY_ID_LIST_FIELD_NUMBER; hash = (53 * hash) + getEntityIdListList().hashCode(); } - hash = (37 * hash) + CAKDDMKAIMD_FIELD_NUMBER; - hash = (53 * hash) + cAKDDMKAIMD_; - hash = (37 * hash) + JHFNDBIHLNB_FIELD_NUMBER; - hash = (53 * hash) + getJHFNDBIHLNB(); + hash = (37 * hash) + STATUS_FIELD_NUMBER; + hash = (53 * hash) + status_; + hash = (37 * hash) + SEED_ID_FIELD_NUMBER; + hash = (53 * hash) + getSeedId(); hash = (37 * hash) + END_TIME_FIELD_NUMBER; hash = (53 * hash) + getEndTime(); - hash = (37 * hash) + KHFGOPCOAGM_FIELD_NUMBER; - hash = (53 * hash) + getKHFGOPCOAGM(); + hash = (37 * hash) + GATHER_POINT_TYPE_FIELD_NUMBER; + hash = (53 * hash) + getGatherPointType(); hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -532,13 +532,13 @@ public final class HomePlantSubFieldDataOuterClass { super.clear(); entityIdList_ = emptyIntList(); bitField0_ = (bitField0_ & ~0x00000001); - cAKDDMKAIMD_ = 0; + status_ = 0; - jHFNDBIHLNB_ = 0; + seedId_ = 0; endTime_ = 0; - kHFGOPCOAGM_ = 0; + gatherPointType_ = 0; return this; } @@ -572,10 +572,10 @@ public final class HomePlantSubFieldDataOuterClass { bitField0_ = (bitField0_ & ~0x00000001); } result.entityIdList_ = entityIdList_; - result.cAKDDMKAIMD_ = cAKDDMKAIMD_; - result.jHFNDBIHLNB_ = jHFNDBIHLNB_; + result.status_ = status_; + result.seedId_ = seedId_; result.endTime_ = endTime_; - result.kHFGOPCOAGM_ = kHFGOPCOAGM_; + result.gatherPointType_ = gatherPointType_; onBuilt(); return result; } @@ -634,17 +634,17 @@ public final class HomePlantSubFieldDataOuterClass { } onChanged(); } - if (other.cAKDDMKAIMD_ != 0) { - setCAKDDMKAIMDValue(other.getCAKDDMKAIMDValue()); + if (other.status_ != 0) { + setStatusValue(other.getStatusValue()); } - if (other.getJHFNDBIHLNB() != 0) { - setJHFNDBIHLNB(other.getJHFNDBIHLNB()); + if (other.getSeedId() != 0) { + setSeedId(other.getSeedId()); } if (other.getEndTime() != 0) { setEndTime(other.getEndTime()); } - if (other.getKHFGOPCOAGM() != 0) { - setKHFGOPCOAGM(other.getKHFGOPCOAGM()); + if (other.getGatherPointType() != 0) { + setGatherPointType(other.getGatherPointType()); } this.mergeUnknownFields(other.unknownFields); onChanged(); @@ -755,87 +755,87 @@ public final class HomePlantSubFieldDataOuterClass { return this; } - private int cAKDDMKAIMD_ = 0; + private int status_ = 0; /** - *.HomePlantFieldStatus CAKDDMKAIMD = 7;
- * @return The enum numeric value on the wire for cAKDDMKAIMD. + *.HomePlantFieldStatus status = 7;
+ * @return The enum numeric value on the wire for status. */ - @java.lang.Override public int getCAKDDMKAIMDValue() { - return cAKDDMKAIMD_; + @java.lang.Override public int getStatusValue() { + return status_; } /** - *.HomePlantFieldStatus CAKDDMKAIMD = 7;
- * @param value The enum numeric value on the wire for cAKDDMKAIMD to set. + *.HomePlantFieldStatus status = 7;
+ * @param value The enum numeric value on the wire for status to set. * @return This builder for chaining. */ - public Builder setCAKDDMKAIMDValue(int value) { + public Builder setStatusValue(int value) { - cAKDDMKAIMD_ = value; + status_ = value; onChanged(); return this; } /** - *.HomePlantFieldStatus CAKDDMKAIMD = 7;
- * @return The cAKDDMKAIMD. + *.HomePlantFieldStatus status = 7;
+ * @return The status. */ @java.lang.Override - public emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getCAKDDMKAIMD() { + public emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus getStatus() { @SuppressWarnings("deprecation") - emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus result = emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.valueOf(cAKDDMKAIMD_); + emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus result = emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.valueOf(status_); return result == null ? emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus.UNRECOGNIZED : result; } /** - *.HomePlantFieldStatus CAKDDMKAIMD = 7;
- * @param value The cAKDDMKAIMD to set. + *.HomePlantFieldStatus status = 7;
+ * @param value The status to set. * @return This builder for chaining. */ - public Builder setCAKDDMKAIMD(emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus value) { + public Builder setStatus(emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.HomePlantFieldStatus value) { if (value == null) { throw new NullPointerException(); } - cAKDDMKAIMD_ = value.getNumber(); + status_ = value.getNumber(); onChanged(); return this; } /** - *.HomePlantFieldStatus CAKDDMKAIMD = 7;
+ *.HomePlantFieldStatus status = 7;
* @return This builder for chaining. */ - public Builder clearCAKDDMKAIMD() { + public Builder clearStatus() { - cAKDDMKAIMD_ = 0; + status_ = 0; onChanged(); return this; } - private int jHFNDBIHLNB_ ; + private int seedId_ ; /** - *uint32 JHFNDBIHLNB = 8;
- * @return The jHFNDBIHLNB. + *uint32 seed_id = 8;
+ * @return The seedId. */ @java.lang.Override - public int getJHFNDBIHLNB() { - return jHFNDBIHLNB_; + public int getSeedId() { + return seedId_; } /** - *uint32 JHFNDBIHLNB = 8;
- * @param value The jHFNDBIHLNB to set. + *uint32 seed_id = 8;
+ * @param value The seedId to set. * @return This builder for chaining. */ - public Builder setJHFNDBIHLNB(int value) { + public Builder setSeedId(int value) { - jHFNDBIHLNB_ = value; + seedId_ = value; onChanged(); return this; } /** - *uint32 JHFNDBIHLNB = 8;
+ *uint32 seed_id = 8;
* @return This builder for chaining. */ - public Builder clearJHFNDBIHLNB() { + public Builder clearSeedId() { - jHFNDBIHLNB_ = 0; + seedId_ = 0; onChanged(); return this; } @@ -871,33 +871,33 @@ public final class HomePlantSubFieldDataOuterClass { return this; } - private int kHFGOPCOAGM_ ; + private int gatherPointType_ ; /** - *uint32 KHFGOPCOAGM = 3;
- * @return The kHFGOPCOAGM. + *uint32 gather_point_type = 3;
+ * @return The gatherPointType. */ @java.lang.Override - public int getKHFGOPCOAGM() { - return kHFGOPCOAGM_; + public int getGatherPointType() { + return gatherPointType_; } /** - *uint32 KHFGOPCOAGM = 3;
- * @param value The kHFGOPCOAGM to set. + *uint32 gather_point_type = 3;
+ * @param value The gatherPointType to set. * @return This builder for chaining. */ - public Builder setKHFGOPCOAGM(int value) { + public Builder setGatherPointType(int value) { - kHFGOPCOAGM_ = value; + gatherPointType_ = value; onChanged(); return this; } /** - *uint32 KHFGOPCOAGM = 3;
+ *uint32 gather_point_type = 3;
* @return This builder for chaining. */ - public Builder clearKHFGOPCOAGM() { + public Builder clearGatherPointType() { - kHFGOPCOAGM_ = 0; + gatherPointType_ = 0; onChanged(); return this; } @@ -969,12 +969,12 @@ public final class HomePlantSubFieldDataOuterClass { static { java.lang.String[] descriptorData = { "\n\033HomePlantSubFieldData.proto\032\032HomePlant" + - "FieldStatus.proto\"\227\001\n\025HomePlantSubFieldD" + - "ata\022\026\n\016entity_id_list\030\006 \003(\r\022*\n\013CAKDDMKAI" + - "MD\030\007 \001(\0162\025.HomePlantFieldStatus\022\023\n\013JHFND" + - "BIHLNB\030\010 \001(\r\022\020\n\010end_time\030\016 \001(\007\022\023\n\013KHFGOP" + - "COAGM\030\003 \001(\rB\033\n\031emu.grasscutter.net.proto" + - "b\006proto3" + "FieldStatus.proto\"\224\001\n\025HomePlantSubFieldD" + + "ata\022\026\n\016entity_id_list\030\006 \003(\r\022%\n\006status\030\007 " + + "\001(\0162\025.HomePlantFieldStatus\022\017\n\007seed_id\030\010 " + + "\001(\r\022\020\n\010end_time\030\016 \001(\007\022\031\n\021gather_point_ty" + + "pe\030\003 \001(\rB\033\n\031emu.grasscutter.net.protob\006p" + + "roto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -986,7 +986,7 @@ public final class HomePlantSubFieldDataOuterClass { internal_static_HomePlantSubFieldData_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_HomePlantSubFieldData_descriptor, - new java.lang.String[] { "EntityIdList", "CAKDDMKAIMD", "JHFNDBIHLNB", "EndTime", "KHFGOPCOAGM", }); + new java.lang.String[] { "EntityIdList", "Status", "SeedId", "EndTime", "GatherPointType", }); emu.grasscutter.net.proto.HomePlantFieldStatusOuterClass.getDescriptor(); } diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 400cade1e..24b9cbb7e 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -286,6 +286,10 @@ public final class GameData { private static final Int2ObjectMaphomeWorldBgmDataMap = new Int2ObjectOpenHashMap<>(); + @Getter + private static final Int2ObjectMap homeWorldEventDataMap = + new Int2ObjectOpenHashMap<>(); + @Getter private static final Int2ObjectMap homeWorldLevelDataMap = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java b/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java new file mode 100644 index 000000000..704ef73ce --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java @@ -0,0 +1,38 @@ +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.home.suite.event.SuiteEventType; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +@ResourceType(name = "HomeWorldEventExcelConfigData.json") +@FieldDefaults(level = AccessLevel.PRIVATE) +@Getter +public class HomeWorldEventData extends GameResource { + @SerializedName( + value = "id", + alternate = {"BBEIIPEFDPE"}) + int id; + + @SerializedName( + value = "eventType", + alternate = {"JOCKIMECHDP"}) + SuiteEventType eventType; + + int avatarID; + + @SerializedName( + value = "talkId", + alternate = {"IGNJAICDFPD"}) + int talkId; + + int rewardID; + + @SerializedName( + value = "suiteId", + alternate = {"FEHOKMJPOED"}) + int suiteId; +} diff --git a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java index 426849aa9..40e61c339 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java @@ -8,6 +8,7 @@ import emu.grasscutter.game.world.Scene; import emu.grasscutter.net.proto.VisionTypeOuterClass; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify; +import java.util.concurrent.atomic.AtomicBoolean; import lombok.Getter; public class EntityHomeAnimal extends EntityMonster implements Rebornable { @@ -15,7 +16,7 @@ public class EntityHomeAnimal extends EntityMonster implements Rebornable { private final Position rebornPos; @Getter private final int rebirth; @Getter private final int rebirthCD; - private boolean disappeared; + private final AtomicBoolean disappeared = new AtomicBoolean(); public EntityHomeAnimal(Scene scene, HomeWorldAnimalData data, Position pos) { super(scene, GameData.getMonsterDataMap().get(data.getMonsterID()), pos, 1); @@ -60,13 +61,13 @@ public class EntityHomeAnimal extends EntityMonster implements Rebornable { new PacketSceneEntityDisappearNotify( this, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE)); this.rebornCDTickCount = this.getRebornCD(); - this.disappeared = true; + this.disappeared.set(true); } @Override public void reborn() { - if (this.disappeared) { - this.disappeared = false; + if (this.disappeared.get()) { + this.disappeared.set(false); this.getPosition().set(this.getRebornPos()); this.getScene().broadcastPacket(new PacketSceneEntityAppearNotify(this)); } @@ -74,6 +75,6 @@ public class EntityHomeAnimal extends EntityMonster implements Rebornable { @Override public boolean isInCD() { - return this.disappeared; + return this.disappeared.get(); } } diff --git a/src/main/java/emu/grasscutter/game/home/GameHome.java b/src/main/java/emu/grasscutter/game/home/GameHome.java index 52f58e3f6..d4a2348c5 100644 --- a/src/main/java/emu/grasscutter/game/home/GameHome.java +++ b/src/main/java/emu/grasscutter/game/home/GameHome.java @@ -17,6 +17,7 @@ import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; @@ -55,6 +56,7 @@ public class GameHome { Set unlockedHomeBgmList; int enterHomeOption; Map > finishedTalkIdMap; + Set finishedRewardEventIdSet; public static GameHome getByUid(Integer uid) { var home = DatabaseHelper.getHomeByUid(uid); @@ -62,7 +64,9 @@ public class GameHome { home = GameHome.create(uid); } + home.reassignIfNull(); home.fixMainHouseIfOld(); + home.syncHomeAvatarCostume(); return home; } @@ -79,9 +83,19 @@ public class GameHome { .mainHouseMap(new ConcurrentHashMap<>()) .unlockedHomeBgmList(new HashSet<>()) .finishedTalkIdMap(new HashMap<>()) + .finishedRewardEventIdSet(new HashSet<>()) .build(); } + // avoid NPE caused by database remover. + private void reassignIfNull() { + this.getSceneMap().values().stream() + .map(HomeSceneItem::getBlockItems) + .map(Map::values) + .flatMap(Collection::stream) + .forEach(HomeBlockItem::reassignIfNull); + } + // Data fixer. private void fixMainHouseIfOld() { if (this.getMainHouseMap() == null) { @@ -97,6 +111,18 @@ public class GameHome { this.save(); } + private void syncHomeAvatarCostume() { + Stream.of(this.sceneMap, this.mainHouseMap) + .map(ConcurrentHashMap::values) + .flatMap(Collection::stream) + .map(HomeSceneItem::getBlockItems) + .map(Map::values) + .flatMap(Collection::stream) + .map(HomeBlockItem::getDeployNPCList) + .flatMap(Collection::stream) + .forEach(npc -> npc.setCostumeId(this.getPlayer().getCostumeFrom(npc.getAvatarId()))); + } + public void save() { DatabaseHelper.saveHome(this); } @@ -113,12 +139,12 @@ public class GameHome { if (defaultItem != null) { Grasscutter.getLogger() .info("Set player {} home {} to initial setting", ownerUid, sceneId); - return HomeSceneItem.parseFrom(defaultItem, sceneId); } else { // Realm res missing bricks account, use default realm data to allow main house defaultItem = GameData.getHomeworldDefaultSaveData().get(2001); - return HomeSceneItem.parseFrom(defaultItem, sceneId); } + + return HomeSceneItem.parseFrom(defaultItem, sceneId); }); } @@ -149,6 +175,8 @@ public class GameHome { this.getMainHouseMap().remove(outdoor); // delete main house in current scene. this.getMainHouseItem(outdoor); // put new main house with default arrangement. this.save(); + + this.getPlayer().getCurHomeWorld().getModuleManager().refreshMainHouse(); } public void onOwnerLogin(Player player) { @@ -160,6 +188,8 @@ public class GameHome { player.getSession().send(new PacketHomeMarkPointNotify(player)); player.getSession().send(new PacketHomeAvatarTalkFinishInfoNotify(player)); player.getSession().send(new PacketHomeAllUnlockedBgmIdListNotify(player)); + player.getSession().send(new PacketHomeAvatarRewardEventNotify(player)); + player.getSession().send(new PacketHomeAvatarAllFinishRewardNotify(player)); checkAccumulatedResources(player); player.getSession().send(new PacketHomeResourceNotify(player)); } @@ -226,6 +256,20 @@ public class GameHome { .toList(); } + public boolean onClaimAvatarRewards(int eventId) { + if (this.finishedRewardEventIdSet == null) { + this.finishedRewardEventIdSet = new HashSet<>(); + } + + var success = this.finishedRewardEventIdSet.add(eventId); + this.save(); + return success; + } + + public boolean isRewardEventFinished(int eventId) { + return this.finishedRewardEventIdSet != null && this.finishedRewardEventIdSet.contains(eventId); + } + public boolean addUnlockedHomeBgm(int homeBgmId) { if (!getUnlockedHomeBgmList().add(homeBgmId)) return false; @@ -404,7 +448,7 @@ public class GameHome { newCoin = storedCoin + owedCoin; } // Ensure max is not exceeded - storedCoin = (maxCoin >= newCoin) ? newCoin : maxCoin; + storedCoin = Math.min(maxCoin, newCoin); } // Update fetter exp @@ -416,7 +460,7 @@ public class GameHome { newFetter = storedFetterExp + owedFetter; } // Ensure max is not exceeded - storedFetterExp = (maxFetter >= newFetter) ? newFetter : maxFetter; + storedFetterExp = Math.min(maxFetter, newFetter); } save(); diff --git a/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java b/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java index dbd080d46..69fe9f50a 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java @@ -2,6 +2,8 @@ package emu.grasscutter.game.home; import dev.morphia.annotations.*; import emu.grasscutter.data.binout.HomeworldDefaultSaveData; +import emu.grasscutter.game.home.suite.HomeSuiteItem; +import emu.grasscutter.game.player.Player; import emu.grasscutter.net.proto.HomeBlockArrangementInfoOuterClass.HomeBlockArrangementInfo; import java.util.*; import java.util.stream.Stream; @@ -19,6 +21,7 @@ public class HomeBlockItem { List persistentFurnitureList; List deployAnimalList; List deployNPCList; + List suiteList; public static HomeBlockItem parseFrom(HomeworldDefaultSaveData.HomeBlock homeBlock) { // create from default setting @@ -37,10 +40,11 @@ public class HomeBlockItem { .toList()) .deployAnimalList(List.of()) .deployNPCList(List.of()) + .suiteList(List.of()) .build(); } - public void update(HomeBlockArrangementInfo homeBlockArrangementInfo) { + public void update(HomeBlockArrangementInfo homeBlockArrangementInfo, Player owner) { this.blockId = homeBlockArrangementInfo.getBlockId(); this.deployFurnitureList = @@ -60,7 +64,12 @@ public class HomeBlockItem { this.deployNPCList = homeBlockArrangementInfo.getDeployNpcListList().stream() - .map(HomeNPCItem::parseFrom) + .map(homeNpcData -> HomeNPCItem.parseFrom(homeNpcData, owner)) + .toList(); + + this.suiteList = + homeBlockArrangementInfo.getFurnitureSuiteListList().stream() + .map(HomeSuiteItem::parseFrom) .toList(); } @@ -81,15 +90,20 @@ public class HomeBlockItem { this.persistentFurnitureList.forEach(f -> proto.addPersistentFurnitureList(f.toProto())); this.deployAnimalList.forEach(f -> proto.addDeployAnimalList(f.toProto())); this.deployNPCList.forEach(f -> proto.addDeployNpcList(f.toProto())); + this.suiteList.forEach(f -> proto.addFurnitureSuiteList(f.toProto())); return proto.build(); } - // TODO add more types (farm field and suite) + // TODO implement farm field. public List extends HomeMarkPointProtoFactory> getMarkPointProtoFactories() { this.reassignIfNull(); - return Stream.of(this.deployFurnitureList, this.persistentFurnitureList, this.deployNPCList) + return Stream.of( + this.deployFurnitureList, + this.persistentFurnitureList, + this.deployNPCList, + this.suiteList) .flatMap(Collection::stream) .toList(); } @@ -107,5 +121,8 @@ public class HomeBlockItem { if (this.deployNPCList == null) { this.deployNPCList = List.of(); } + if (this.suiteList == null) { + this.suiteList = List.of(); + } } } diff --git a/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java new file mode 100644 index 000000000..26460c09b --- /dev/null +++ b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java @@ -0,0 +1,235 @@ +package emu.grasscutter.game.home; + +import com.github.davidmoten.guavamini.Lists; +import emu.grasscutter.game.home.suite.event.HomeAvatarRewardEvent; +import emu.grasscutter.game.home.suite.event.HomeAvatarSummonEvent; +import emu.grasscutter.game.home.suite.event.SuiteEventType; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.proto.HomeAvatarRewardEventNotifyOuterClass; +import emu.grasscutter.net.proto.HomeAvatarSummonAllEventNotifyOuterClass; +import emu.grasscutter.net.proto.RetcodeOuterClass; +import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonAllEventNotify; +import emu.grasscutter.utils.Either; +import java.util.*; +import java.util.stream.Stream; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +public class HomeModuleManager { + final Player homeOwner; + final HomeWorld homeWorld; + final GameHome home; + final int moduleId; + final HomeScene outdoor; + HomeScene indoor; + final List rewardEvents; + final List summonEvents; + + public HomeModuleManager(HomeWorld homeWorld) { + this.homeOwner = homeWorld.getHost(); + this.homeWorld = homeWorld; + this.home = homeWorld.getHome(); + this.moduleId = this.homeOwner.getCurrentRealmId(); + this.outdoor = homeWorld.getSceneById(homeWorld.getActiveOutdoorSceneId()); + this.refreshMainHouse(); + this.rewardEvents = Lists.newArrayList(); + this.summonEvents = Collections.synchronizedList(Lists.newArrayList()); + } + + public void tick() { + if (this.moduleId == 0) { + return; + } + + this.outdoor.onTick(); + this.indoor.onTick(); + this.summonEvents.removeIf(HomeAvatarSummonEvent::isTimeOver); + } + + public void refreshMainHouse() { + if (this.moduleId == 0) { + return; + } + + this.indoor = this.homeWorld.getSceneById(this.homeWorld.getActiveIndoorSceneId()); + } + + public void onUpdateArrangement() { + this.fireAllAvatarRewardEvent(); + this.cancelSummonEventIfAvatarLeave(); + } + + private void fireAllAvatarRewardEvent() { + this.rewardEvents.clear(); + var allBlockItems = + Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem()) + .map(HomeSceneItem::getBlockItems) + .map(Map::values) + .flatMap(Collection::stream) + .toList(); + + var suites = + allBlockItems.stream() + .map(HomeBlockItem::getSuiteList) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .distinct() + .toList(); + + allBlockItems.stream() + .map(HomeBlockItem::getDeployNPCList) + .flatMap(Collection::stream) + .forEach( + avatar -> { + suites.forEach( + suite -> { + var data = + SuiteEventType.HOME_AVATAR_REWARD_EVENT.getEventDataFrom( + avatar.getAvatarId(), suite.getSuiteId()); + if (data == null || this.home.isRewardEventFinished(data.getId())) { + return; + } + + this.rewardEvents.add( + new HomeAvatarRewardEvent( + homeOwner, + data.getId(), + data.getRewardID(), + data.getAvatarID(), + data.getSuiteId(), + suite.getGuid())); + }); + }); + + if (this.summonEvents != null) { + var suiteIdList = this.rewardEvents.stream().map(HomeAvatarRewardEvent::getSuiteId).toList(); + this.summonEvents.removeIf(event -> suiteIdList.contains(event.getSuiteId())); + } + } + + private void cancelSummonEventIfAvatarLeave() { + var avatars = + Stream.of(this.getOutdoorSceneItem(), this.getIndoorSceneItem()) + .map(HomeSceneItem::getBlockItems) + .map(Map::values) + .flatMap(Collection::stream) + .map(HomeBlockItem::getDeployNPCList) + .flatMap(Collection::stream) + .map(HomeNPCItem::getAvatarId) + .toList(); + + this.summonEvents.removeIf(event -> !avatars.contains(event.getAvatarId())); + } + + public Either , Integer> claimAvatarRewards(int eventId) { + if (this.rewardEvents.isEmpty()) { + return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE); + } + + var event = this.rewardEvents.remove(0); + if (event.getEventId() != eventId) { + return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE); + } + + if (!this.homeOwner.getHome().onClaimAvatarRewards(eventId)) { + return Either.right(RetcodeOuterClass.Retcode.RET_FAIL_VALUE); + } + + return Either.left(event.giveRewards()); + } + + public Either
fireAvatarSummonEvent( + Player owner, int avatarId, int guid, int suiteId) { + var targetSuite = + ((HomeScene) owner.getScene()) + .getSceneItem().getBlockItems().values().stream() + .map(HomeBlockItem::getSuiteList) + .flatMap(Collection::stream) + .filter(suite -> suite.getGuid() == guid) + .findFirst() + .orElse(null); + + if (this.isInRewardEvent(avatarId)) { + return Either.right(RetcodeOuterClass.Retcode.RET_DUPLICATE_AVATAR_VALUE); + } + + if (this.rewardEvents.stream().anyMatch(event -> event.getGuid() == guid)) { + return Either.right(RetcodeOuterClass.Retcode.RET_HOME_FURNITURE_GUID_ERROR_VALUE); + } + + this.summonEvents.removeIf(event -> event.getGuid() == guid || event.getAvatarId() == avatarId); + + if (targetSuite == null) { + return Either.right(RetcodeOuterClass.Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE); + } + + var eventData = SuiteEventType.HOME_AVATAR_SUMMON_EVENT.getEventDataFrom(avatarId, suiteId); + if (eventData == null) { + return Either.right(RetcodeOuterClass.Retcode.RET_HOME_CLIENT_PARAM_INVALID_VALUE); + } + + var event = + new HomeAvatarSummonEvent( + owner, eventData.getId(), eventData.getRewardID(), avatarId, suiteId, guid); + this.summonEvents.add(event); + owner.sendPacket(new PacketHomeAvatarSummonAllEventNotify(owner)); + return Either.left(event); + } + + public void onFinishSummonEvent(int eventId) { + this.summonEvents.removeIf(event -> event.getEventId() == eventId); + } + + public HomeAvatarRewardEventNotifyOuterClass.HomeAvatarRewardEventNotify toRewardEventProto() { + var notify = HomeAvatarRewardEventNotifyOuterClass.HomeAvatarRewardEventNotify.newBuilder(); + if (!this.rewardEvents.isEmpty()) { + notify.setRewardEvent(this.rewardEvents.get(0).toProto()).setIsEventTrigger(true); + + notify.addAllPendingList( + this.rewardEvents.subList(1, this.rewardEvents.size()).stream() + .map(HomeAvatarRewardEvent::toProto) + .toList()); + } + + return notify.build(); + } + + public HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify + toSummonEventProto() { + return HomeAvatarSummonAllEventNotifyOuterClass.HomeAvatarSummonAllEventNotify.newBuilder() + .addAllSummonEventList( + this.summonEvents.stream().map(HomeAvatarSummonEvent::toProto).toList()) + .build(); + } + + public boolean isInRewardEvent(int avatarId) { + return this.rewardEvents.stream().anyMatch(e -> e.getAvatarId() == avatarId); + } + + public HomeSceneItem getOutdoorSceneItem() { + return this.outdoor.getSceneItem(); + } + + public HomeSceneItem getIndoorSceneItem() { + return this.indoor.getSceneItem(); + } + + public void onSetModule() { + if (this.moduleId == 0) { + return; + } + + this.outdoor.addEntities(this.getOutdoorSceneItem().getAnimals(this.outdoor)); + this.indoor.addEntities(this.getIndoorSceneItem().getAnimals(this.indoor)); + this.fireAllAvatarRewardEvent(); + } + + public void onRemovedModule() { + this.outdoor.getEntities().clear(); + this.indoor.getEntities().clear(); + } +} diff --git a/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java b/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java index fbd2ad377..7999fcd39 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java @@ -2,6 +2,7 @@ package emu.grasscutter.game.home; import dev.morphia.annotations.Entity; import emu.grasscutter.data.GameData; +import emu.grasscutter.game.player.Player; import emu.grasscutter.game.world.Position; import emu.grasscutter.net.proto.HomeMarkPointFurnitureDataOuterClass; import emu.grasscutter.net.proto.HomeMarkPointNPCDataOuterClass; @@ -23,11 +24,12 @@ public class HomeNPCItem implements HomeMarkPointProtoFactory { Position spawnRot; int costumeId; - public static HomeNPCItem parseFrom(HomeNpcDataOuterClass.HomeNpcData homeNpcData) { + public static HomeNPCItem parseFrom(HomeNpcDataOuterClass.HomeNpcData homeNpcData, Player owner) { return HomeNPCItem.of() .avatarId(homeNpcData.getAvatarId()) .spawnPos(new Position(homeNpcData.getSpawnPos())) .spawnRot(new Position(homeNpcData.getSpawnRot())) + .costumeId(owner.getCostumeFrom(homeNpcData.getAvatarId())) .build(); } diff --git a/src/main/java/emu/grasscutter/game/home/HomeScene.java b/src/main/java/emu/grasscutter/game/home/HomeScene.java index b07f798b0..c03247c1d 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeScene.java +++ b/src/main/java/emu/grasscutter/game/home/HomeScene.java @@ -1,9 +1,12 @@ package emu.grasscutter.game.home; import emu.grasscutter.data.excels.scene.SceneData; +import emu.grasscutter.game.entity.EntityHomeAnimal; import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.entity.Rebornable; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.world.Scene; +import emu.grasscutter.net.proto.VisionTypeOuterClass; import emu.grasscutter.server.packet.send.PacketSceneTimeNotify; public class HomeScene extends Scene { @@ -40,10 +43,31 @@ public class HomeScene extends Scene { .forEach(gameEntity -> gameEntity.onTick(this.getSceneTimeSeconds())); this.finishLoading(); - this.checkPlayerRespawn(); if (this.tickCount++ % 10 == 0) this.broadcastPacket(new PacketSceneTimeNotify(this)); } + public void onEnterEditModeFinish() { + this.removeEntities( + this.getEntities().values().stream() + .filter(gameEntity -> gameEntity instanceof EntityHomeAnimal) + .toList(), + VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE); + } + + public void onLeaveEditMode() { + this.addEntities(this.getSceneItem().getAnimals(this)); + } + + @Override + public void killEntity(GameEntity target, int attackerId) { + if (target instanceof Rebornable rebornable) { + rebornable.onAiKillSelf(); // Teapot animals will not die. They will revive! + return; + } + + super.killEntity(target, attackerId); + } + @Override public void checkNpcGroup() {} diff --git a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java index 7449f029f..93031537c 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java @@ -6,16 +6,18 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; import emu.grasscutter.data.binout.HomeworldDefaultSaveData; import emu.grasscutter.game.entity.EntityHomeAnimal; +import emu.grasscutter.game.player.Player; import emu.grasscutter.game.world.Position; import emu.grasscutter.game.world.Scene; import emu.grasscutter.net.proto.HomeSceneArrangementInfoOuterClass.HomeSceneArrangementInfo; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import javax.annotation.Nullable; -import lombok.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Data; import lombok.experimental.FieldDefaults; @Entity @@ -49,14 +51,14 @@ public class HomeSceneItem { .build(); } - public void update(HomeSceneArrangementInfo arrangementInfo) { + public void update(HomeSceneArrangementInfo arrangementInfo, Player owner) { for (var blockItem : arrangementInfo.getBlockArrangementInfoListList()) { var block = this.blockItems.get(blockItem.getBlockId()); if (block == null) { Grasscutter.getLogger().warn("Could not found the Home Block {}", blockItem.getBlockId()); continue; } - block.update(blockItem); + block.update(blockItem, owner); this.blockItems.put(blockItem.getBlockId(), block); } @@ -84,17 +86,13 @@ public class HomeSceneItem { } @Nullable public Position getTeleportPointPos(int guid) { - var pos = new AtomicReference (); - - this.getBlockItems().values().stream() + return this.getBlockItems().values().stream() .map(HomeBlockItem::getDeployFurnitureList) .flatMap(Collection::stream) .filter(homeFurnitureItem -> homeFurnitureItem.getGuid() == guid) .map(HomeFurnitureItem::getSpawnPos) .findFirst() - .ifPresent(pos::set); - - return pos.get(); + .orElse(null); } public List getAnimals(Scene scene) { diff --git a/src/main/java/emu/grasscutter/game/home/HomeWorld.java b/src/main/java/emu/grasscutter/game/home/HomeWorld.java index f2ea9d3b6..525da1be4 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeWorld.java +++ b/src/main/java/emu/grasscutter/game/home/HomeWorld.java @@ -8,33 +8,59 @@ import emu.grasscutter.game.world.World; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.proto.ChatInfoOuterClass; import emu.grasscutter.server.game.GameServer; -import emu.grasscutter.server.packet.send.*; +import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify; +import emu.grasscutter.server.packet.send.PacketPlayerChatNotify; +import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify; import java.util.List; +import java.util.function.Consumer; import lombok.Getter; public class HomeWorld extends World { @Getter private final GameHome home; + @Getter private HomeModuleManager moduleManager; public HomeWorld(GameServer server, Player owner) { super(server, owner); this.home = owner.isOnline() ? owner.getHome() : GameHome.getByUid(owner.getUid()); server.registerHomeWorld(this); + this.refreshModuleManager(); } @Override - public void registerScene(Scene scene) { - this.addAnimalsToScene((HomeScene) scene); - super.registerScene(scene); + public boolean onTick() { + if (this.moduleManager == null) { + return false; + } + this.moduleManager.tick(); + + if (this.getTickCount() % 10 == 0) { + this.getPlayers().forEach(p -> p.sendPacket(new PacketPlayerGameTimeNotify(p))); + } + + if (this.isInHome(this.getHost()) && this.getTickCount() % 60 == 0) { + this.getHost().updatePlayerGameTime(this.getCurrentWorldTime()); + } + + this.tickCount++; + return false; } - @Override - public void deregisterScene(Scene scene) { - super.deregisterScene(scene); + public void refreshModuleManager() { + if (this.moduleManager != null) { + this.moduleManager.onRemovedModule(); + } + + this.moduleManager = new HomeModuleManager(this); + this.moduleManager.onSetModule(); } - private void addAnimalsToScene(HomeScene scene) { - scene.getSceneItem().getAnimals(scene).forEach(scene::addEntity); + public int getActiveOutdoorSceneId() { + return this.getHost().getCurrentRealmId() + 2000; + } + + public int getActiveIndoorSceneId() { + return this.getSceneById(this.getActiveOutdoorSceneId()).getSceneItem().getRoomSceneId(); } @Override @@ -188,6 +214,12 @@ public class HomeWorld extends World { return this.getPlayers().contains(player); } + public void ifHost(Player hostOrGuest, Consumer ifHost) { + if (this.getHost().equals(hostOrGuest)) { + ifHost.accept(hostOrGuest); + } + } + public void sendPacketToHostIfOnline(BasePacket basePacket) { if (this.getHost().isOnline()) { this.getHost().sendPacket(basePacket); diff --git a/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java b/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java index aecacb21c..0835975b0 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeWorldMPSystem.java @@ -5,10 +5,7 @@ import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.game.world.Position; import emu.grasscutter.game.world.World; import emu.grasscutter.game.world.data.TeleportProperties; -import emu.grasscutter.net.proto.EnterTypeOuterClass; -import emu.grasscutter.net.proto.OtherPlayerEnterHomeNotifyOuterClass; -import emu.grasscutter.net.proto.PlayerApplyEnterHomeResultNotifyOuterClass; -import emu.grasscutter.net.proto.RetcodeOuterClass; +import emu.grasscutter.net.proto.*; import emu.grasscutter.server.event.player.PlayerEnterHomeEvent; import emu.grasscutter.server.event.player.PlayerLeaveHomeEvent; import emu.grasscutter.server.event.player.PlayerTeleportEvent; @@ -215,6 +212,10 @@ public class HomeWorldMPSystem extends BaseGameSystem { player.setCurHomeWorld(myHome); myHome.getHome().onOwnerLogin(player); + player.sendPacket( + new PacketPlayerQuitFromHomeNotify( + PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason + .BACK_TO_MY_WORLD)); player.sendPacket( new PacketPlayerEnterSceneNotify( player, @@ -263,6 +264,9 @@ public class HomeWorldMPSystem extends BaseGameSystem { victim.setCurHomeWorld(myHome); myHome.getHome().onOwnerLogin(victim); + victim.sendPacket( + new PacketPlayerQuitFromHomeNotify( + PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason.KICK_BY_HOST)); victim.sendPacket( new PacketPlayerEnterSceneNotify( victim, diff --git a/src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java b/src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java new file mode 100644 index 000000000..88841e9fb --- /dev/null +++ b/src/main/java/emu/grasscutter/game/home/suite/HomeSuiteItem.java @@ -0,0 +1,82 @@ +package emu.grasscutter.game.home.suite; + +import dev.morphia.annotations.Entity; +import emu.grasscutter.game.home.HomeMarkPointProtoFactory; +import emu.grasscutter.game.home.SpecialFurnitureType; +import emu.grasscutter.game.world.Position; +import emu.grasscutter.net.proto.HomeFurnitureSuiteDataOuterClass; +import emu.grasscutter.net.proto.HomeMarkPointFurnitureDataOuterClass; +import emu.grasscutter.net.proto.HomeMarkPointSuiteDataOuterClass; +import java.util.List; +import java.util.Objects; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.experimental.FieldDefaults; +import org.jetbrains.annotations.Nullable; + +@Entity +@Builder(builderMethodName = "of") +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +public class HomeSuiteItem implements HomeMarkPointProtoFactory { + public static final int SUITE_FURNITURE_ID = 377101; + int guid; + int suiteId; + Position pos; + List includedFurnitureIndexList; + boolean isAllowSummon; + + public static HomeSuiteItem parseFrom( + HomeFurnitureSuiteDataOuterClass.HomeFurnitureSuiteData data) { + return HomeSuiteItem.of() + .guid(data.getGuid()) + .suiteId(data.getSuiteId()) + .pos(new Position(data.getSpawnPos())) + .includedFurnitureIndexList(data.getIncludedFurnitureIndexListList()) + .isAllowSummon(data.getIsAllowSummon()) + .build(); + } + + public HomeFurnitureSuiteDataOuterClass.HomeFurnitureSuiteData toProto() { + return HomeFurnitureSuiteDataOuterClass.HomeFurnitureSuiteData.newBuilder() + .setSuiteId(this.suiteId) + .setGuid(this.guid) + .setIsAllowSummon(this.isAllowSummon) + .addAllIncludedFurnitureIndexList(this.includedFurnitureIndexList) + .setSpawnPos(this.pos.toProto()) + .build(); + } + + @Nullable @Override + public HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData toMarkPointProto() { + return HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData.newBuilder() + .setFurnitureId(SUITE_FURNITURE_ID) + .setPos(this.pos.toProto()) + .setFurnitureType(this.getType().getValue()) + .setGuid(this.guid) + .setSuiteData( + HomeMarkPointSuiteDataOuterClass.HomeMarkPointSuiteData.newBuilder() + .setSuiteId(this.suiteId) + .build()) + .build(); + } + + @Override + public SpecialFurnitureType getType() { + return SpecialFurnitureType.FurnitureSuite; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + HomeSuiteItem that = (HomeSuiteItem) o; + return suiteId == that.suiteId; + } + + @Override + public int hashCode() { + return Objects.hash(suiteId); + } +} diff --git a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java new file mode 100644 index 000000000..06d2fc2a9 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarEvent.java @@ -0,0 +1,54 @@ +package emu.grasscutter.game.home.suite.event; + +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.utils.Utils; +import java.util.List; +import java.util.Objects; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +public abstract class HomeAvatarEvent { + final Player homeOwner; + final int eventId; + final int rewardId; + final int avatarId; + final int suiteId; + final int guid; + final int randomPos; + + public HomeAvatarEvent( + Player homeOwner, int eventId, int rewardId, int avatarId, int suiteId, int guid) { + this.homeOwner = homeOwner; + this.eventId = eventId; + this.rewardId = rewardId; + this.avatarId = avatarId; + this.suiteId = suiteId; + this.guid = guid; + this.randomPos = this.generateRandomPos(); + } + + public int generateRandomPos() { + return Utils.randomRange(1, 97); + } + + public List giveRewards() { + return List.of(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + HomeAvatarEvent that = (HomeAvatarEvent) o; + return eventId == that.eventId; + } + + @Override + public int hashCode() { + return Objects.hash(eventId); + } +} diff --git a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java new file mode 100644 index 000000000..de897faf0 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarRewardEvent.java @@ -0,0 +1,37 @@ +package emu.grasscutter.game.home.suite.event; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.net.proto.HomeAvatarRewardEventInfoOuterClass; +import java.util.List; + +public class HomeAvatarRewardEvent extends HomeAvatarEvent { + public HomeAvatarRewardEvent( + Player homeOwner, int eventId, int rewardId, int avatarId, int suiteId, int guid) { + super(homeOwner, eventId, rewardId, avatarId, suiteId, guid); + } + + public HomeAvatarRewardEventInfoOuterClass.HomeAvatarRewardEventInfo toProto() { + return HomeAvatarRewardEventInfoOuterClass.HomeAvatarRewardEventInfo.newBuilder() + .setAvatarId(this.getAvatarId()) + .setEventId(this.getEventId()) + .setGuid(this.getGuid()) + .setSuiteId(this.getSuiteId()) + .setRandomPosition(this.getRandomPos()) + .build(); + } + + @Override + public List giveRewards() { + var data = GameData.getRewardDataMap().get(this.getRewardId()); + if (data == null) { + return List.of(); + } + + var rewards = data.getRewardItemList().stream().map(GameItem::new).toList(); + this.getHomeOwner().getInventory().addItems(rewards, ActionReason.HomeAvatarEventReward); + return rewards; + } +} diff --git a/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java new file mode 100644 index 000000000..f6b9c0b5e --- /dev/null +++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java @@ -0,0 +1,37 @@ +package emu.grasscutter.game.home.suite.event; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.proto.HomeAvatarSummonEventInfoOuterClass; +import emu.grasscutter.utils.Utils; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +public class HomeAvatarSummonEvent extends HomeAvatarEvent { + public static final int TIME_LIMIT_SECS = 240; + final int eventOverTime; + + public HomeAvatarSummonEvent( + Player homeOwner, int eventId, int rewardId, int avatarId, int suiteId, int guid) { + super(homeOwner, eventId, rewardId, avatarId, suiteId, guid); + + this.eventOverTime = Utils.getCurrentSeconds() + TIME_LIMIT_SECS; + } + + public HomeAvatarSummonEventInfoOuterClass.HomeAvatarSummonEventInfo toProto() { + return HomeAvatarSummonEventInfoOuterClass.HomeAvatarSummonEventInfo.newBuilder() + .setAvatarId(this.getAvatarId()) + .setEventId(this.getEventId()) + .setGuid(this.getGuid()) + .setSuitId(this.getSuiteId()) + .setRandomPosition(this.getRandomPos()) + .setEventOverTime(this.eventOverTime) + .build(); + } + + public boolean isTimeOver() { + return Utils.getCurrentSeconds() > this.eventOverTime; + } +} diff --git a/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java b/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java new file mode 100644 index 000000000..6056744e8 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java @@ -0,0 +1,21 @@ +package emu.grasscutter.game.home.suite.event; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.HomeWorldEventData; +import javax.annotation.Nullable; + +public enum SuiteEventType { + HOME_AVATAR_REWARD_EVENT, + HOME_AVATAR_SUMMON_EVENT; + + @Nullable public HomeWorldEventData getEventDataFrom(int avatarId, int suiteId) { + return GameData.getHomeWorldEventDataMap().values().stream() + .filter( + data -> + data.getEventType() == this + && data.getAvatarID() == avatarId + && data.getSuiteId() == suiteId) + .findFirst() + .orElse(null); + } +} diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index d4b242d31..5adc13b80 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -950,6 +950,13 @@ public class Player implements PlayerHook, FieldFetch { this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId)); } + public int getCostumeFrom(int avatarId) { + var avatars = this.getAvatars(); + avatars.loadFromDatabase(); + var avatar = avatars.getAvatarById(avatarId); + return avatar == null ? 0 : avatar.getCostume(); + } + public void addPersonalLine(int personalLineId) { this.getPersonalLineList().add(personalLineId); session.getPlayer().getQuestManager().queueEvent(QuestCond.QUEST_COND_PERSONAL_LINE_UNLOCK, personalLineId); diff --git a/src/main/java/emu/grasscutter/game/props/ActionReason.java b/src/main/java/emu/grasscutter/game/props/ActionReason.java index 76a2aed58..57867ed35 100644 --- a/src/main/java/emu/grasscutter/game/props/ActionReason.java +++ b/src/main/java/emu/grasscutter/game/props/ActionReason.java @@ -177,7 +177,8 @@ public enum ActionReason { ChannellerSlabLoopDungeonFirstPassReward(1090), ChannellerSlabLoopDungeonScoreReward(1091), HomeLimitedShopBuy(1092), - HomeCoinCollect(1093); + HomeCoinCollect(1093), + HomeAvatarEventReward(1100); private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); private static final Map stringMap = new HashMap<>(); diff --git a/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java b/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java index 06acd48e3..b258b8310 100644 --- a/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java +++ b/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java @@ -169,23 +169,6 @@ public class GameMainQuest { this.state = ParentQuestState.PARENT_QUEST_STATE_FINISHED; } - /* - * We also need to check for unfinished childQuests in this MainQuest - * force them to complete and send a packet about this to the user, - * because at some points there are special "invisible" child quests that control - * some situations. - * - * For example, subQuest 35312 is responsible for the event of leaving the territory - * of the island with a statue and automatically returns the character back, - * quest 35311 completes the main quest line 353 and starts 35501 from - * new MainQuest 355 but if 35312 is not completed after the completion - * of the main quest 353 - the character will not be able to leave place - * (return again and again) - */ - // this.getChildQuests().values().stream() - // .filter(p -> p.state != QuestState.QUEST_STATE_FINISHED) - // .forEach(GameQuest::finish); - this.getOwner().getSession().send(new PacketFinishedParentQuestUpdateNotify(this)); this.getOwner().getSession().send(new PacketCodexDataUpdateNotify(this)); @@ -383,46 +366,6 @@ public class GameMainQuest { } } - public void tryAcceptSubQuests(QuestCond condType, String paramStr, int... params) { - try { - List subQuestsWithCond = - getChildQuests().values().stream() - .filter( - p -> - p.getState() == QuestState.QUEST_STATE_UNSTARTED - || p.getState() == QuestState.UNFINISHED) - .filter( - p -> - p.getQuestData().getAcceptCond().stream() - .anyMatch( - q -> - condType == QuestCond.QUEST_COND_NONE || q.getType() == condType)) - .toList(); - var questSystem = owner.getServer().getQuestSystem(); - - for (GameQuest subQuestWithCond : subQuestsWithCond) { - var acceptCond = subQuestWithCond.getQuestData().getAcceptCond(); - int[] accept = new int[acceptCond.size()]; - - for (int i = 0; i < subQuestWithCond.getQuestData().getAcceptCond().size(); i++) { - var condition = acceptCond.get(i); - boolean result = - questSystem.triggerCondition( - getOwner(), subQuestWithCond.getQuestData(), condition, paramStr, params); - accept[i] = result ? 1 : 0; - } - - boolean shouldAccept = - LogicType.calculate(subQuestWithCond.getQuestData().getAcceptCondComb(), accept); - - if (shouldAccept) subQuestWithCond.start(); - } - this.save(); - } catch (Exception e) { - Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e); - } - } - public void tryFailSubQuests(QuestContent condType, String paramStr, int... params) { try { List subQuestsWithCond = @@ -437,7 +380,7 @@ public class GameMainQuest { for (GameQuest subQuestWithCond : subQuestsWithCond) { val failCond = subQuestWithCond.getQuestData().getFailCond(); - for (int i = 0; i < subQuestWithCond.getQuestData().getFailCond().size(); i++) { + for (int i = 0; i < failCond.size(); i++) { val condition = failCond.get(i); if (condition.getType() == condType) { boolean result = @@ -445,7 +388,7 @@ public class GameMainQuest { .getServer() .getQuestSystem() .triggerContent(subQuestWithCond, condition, paramStr, params); - subQuestWithCond.getFailProgressList()[i] = result ? 1 : 0; + subQuestWithCond.setFailProgress(i, result ? 1 : 0); if (result) { getOwner().getSession().send(new PacketQuestProgressUpdateNotify(subQuestWithCond)); } diff --git a/src/main/java/emu/grasscutter/game/quest/GameQuest.java b/src/main/java/emu/grasscutter/game/quest/GameQuest.java index c62b3c0ef..7888e4701 100644 --- a/src/main/java/emu/grasscutter/game/quest/GameQuest.java +++ b/src/main/java/emu/grasscutter/game/quest/GameQuest.java @@ -158,6 +158,13 @@ public class GameQuest { public boolean clearProgress(boolean notifyDelete) { // TODO improve var oldState = state; + if (questData.getAcceptCond() != null && questData.getAcceptCond().size() != 0) { + this.getMainQuest() + .getQuestManager() + .getAcceptProgressLists() + .put(this.getSubQuestId(), new int[questData.getAcceptCond().size()]); + } + if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) { for (var condition : questData.getFinishCond()) { if (condition.getType() == QuestContent.QUEST_CONTENT_LUA_NOTIFY) { diff --git a/src/main/java/emu/grasscutter/game/quest/QuestManager.java b/src/main/java/emu/grasscutter/game/quest/QuestManager.java index 6611da1c7..a14350327 100644 --- a/src/main/java/emu/grasscutter/game/quest/QuestManager.java +++ b/src/main/java/emu/grasscutter/game/quest/QuestManager.java @@ -26,6 +26,7 @@ public final class QuestManager extends BasePlayerManager { @Getter private final Player player; @Getter private final Int2ObjectMap mainQuests; + @Getter private Int2ObjectMap acceptProgressLists; @Getter private final List loggedQuests; private long lastHourCheck = 0; @@ -52,6 +53,7 @@ public final class QuestManager extends BasePlayerManager { this.player = player; this.mainQuests = new Int2ObjectOpenHashMap<>(); this.loggedQuests = new ArrayList<>(); + this.acceptProgressLists = new Int2ObjectOpenHashMap<>(); if (DEBUG) { this.loggedQuests.addAll( @@ -484,8 +486,6 @@ public final class QuestManager extends BasePlayerManager { eventExecutor.submit(() -> triggerEvent(condType, paramStr, params)); } - // QUEST_EXEC are handled directly by each subQuest - public void triggerEvent(QuestCond condType, String paramStr, int... params) { Grasscutter.getLogger().trace("Trigger Event {}, {}, {}", condType, paramStr, params); var potentialQuests = GameData.getQuestDataByConditions(condType, params[0], paramStr); @@ -502,15 +502,19 @@ public final class QuestManager extends BasePlayerManager { return; } val acceptCond = questData.getAcceptCond(); - int[] accept = new int[acceptCond.size()]; + acceptProgressLists.putIfAbsent(questData.getId(), new int[acceptCond.size()]); for (int i = 0; i < acceptCond.size(); i++) { val condition = acceptCond.get(i); - boolean result = - questSystem.triggerCondition(owner, questData, condition, paramStr, params); - accept[i] = result ? 1 : 0; + if (condition.getType() == condType) { + boolean result = + questSystem.triggerCondition(owner, questData, condition, paramStr, params); + acceptProgressLists.get(questData.getId())[i] = result ? 1 : 0; + } } - boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept); + boolean shouldAccept = + LogicType.calculate( + questData.getAcceptCondComb(), acceptProgressLists.get(questData.getId())); if (this.loggedQuests.contains(questData.getId())) { Grasscutter.getLogger() .debug( @@ -522,7 +526,7 @@ public final class QuestManager extends BasePlayerManager { Arrays.stream(params) .mapToObj(String::valueOf) .collect(Collectors.joining(", "))); - for (var i = 0; i < accept.length; i++) { + for (var i = 0; i < acceptCond.size(); i++) { var condition = acceptCond.get(i); Grasscutter.getLogger() .debug( @@ -532,14 +536,13 @@ public final class QuestManager extends BasePlayerManager { .filter(value -> value > 0) .mapToObj(String::valueOf) .collect(Collectors.joining(", ")), - accept[i] == 1 ? "success" : "failure"); + acceptProgressLists.get(questData.getId())[i] == 1 ? "success" : "failure"); } } if (shouldAccept) { GameQuest quest = owner.getQuestManager().addQuest(questData); - Grasscutter.getLogger() - .debug("Added quest {} result {}", questData.getSubId(), quest != null); + Grasscutter.getLogger().debug("Added quest {}", questData.getSubId()); } }); } diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java new file mode 100644 index 000000000..fe77519cb --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemGivingFinished.java @@ -0,0 +1,21 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.data.excels.quest.QuestData; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.QuestValueCond; +import emu.grasscutter.game.quest.enums.QuestCond; + +@QuestValueCond(QuestCond.QUEST_COND_ITEM_GIVING_FINISHED) +public class ConditionItemGivingFinished extends BaseCondition { + + @Override + public boolean execute( + Player owner, + QuestData questData, + QuestData.QuestAcceptCondition condition, + String paramStr, + int... params) { + return condition.getParam()[0] == params[0] + && (condition.getParam()[1] == 0 || condition.getParam()[1] == params[1]); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentFinishGivingItem.java b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java similarity index 66% rename from src/main/java/emu/grasscutter/game/quest/content/ContentFinishGivingItem.java rename to src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java index cd9ed53cd..01e3171b7 100644 --- a/src/main/java/emu/grasscutter/game/quest/content/ContentFinishGivingItem.java +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishItemGiving.java @@ -5,10 +5,11 @@ import emu.grasscutter.game.quest.*; import emu.grasscutter.game.quest.enums.QuestContent; @QuestValueContent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING) -public final class ContentFinishGivingItem extends BaseContent { +public final class ContentFinishItemGiving extends BaseContent { @Override public boolean execute( GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) { - return condition.getParam()[0] == params[0] && condition.getParam()[1] == params[1]; + return condition.getParam()[0] == params[0] + && (condition.getParam()[1] == 0 || condition.getParam()[1] == params[1]); } } diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java index 190b313e2..7b43c442d 100644 --- a/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java @@ -25,7 +25,7 @@ public enum QuestCond implements QuestTrigger { QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER(17), QUEST_COND_SCENE_AREA_UNLOCKED(18), // missing, only NPC groups/talks QUEST_COND_ITEM_GIVING_ACTIVED(19), // missing - QUEST_COND_ITEM_GIVING_FINISHED(20), // missing + QUEST_COND_ITEM_GIVING_FINISHED(20), QUEST_COND_IS_DAYTIME(21), // only NPC groups QUEST_COND_CURRENT_AVATAR(22), // missing QUEST_COND_CURRENT_AREA(23), // missing diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index eb0bd86ce..f9c629031 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -535,7 +535,17 @@ public class Scene { "Can not solve monster drop: drop_id = {}, drop_tag = {}. Falling back to legacy drop system.", monster.getMetaMonster().drop_id, monster.getMetaMonster().drop_tag); - getWorld().getServer().getDropSystemLegacy().callDrop(monster); + world.getServer().getDropSystemLegacy().callDrop(monster); + } + } + + if (target instanceof EntityGadget gadget) { + if (gadget.getMetaGadget() != null) { + world + .getServer() + .getDropSystem() + .handleChestDrop( + gadget.getMetaGadget().drop_id, gadget.getMetaGadget().drop_count, gadget); } } diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java index 3b9e01ed0..085e60fe7 100644 --- a/src/main/java/emu/grasscutter/game/world/World.java +++ b/src/main/java/emu/grasscutter/game/world/World.java @@ -39,7 +39,7 @@ public class World implements Iterable { @Getter private boolean timeLocked; private long lastUpdateTime; - @Getter private int tickCount = 0; + @Getter protected int tickCount = 0; @Getter private boolean isPaused = false; @Getter private long currentWorldTime; diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 984c994ba..990ac11b4 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -1105,6 +1105,12 @@ public class ScriptLib { return 0; } + public int MoveAvatarByPointArrayWithTemplate(int uid, int pointarray_id, int[] routelist, int var4, LuaTable var5){ + logger.warn("[LUA] Call unimplemented MoveAvatarByPointArrayWithTemplate with {} {} {} {} {}", uid, pointarray_id, routelist, var4, printTable(var5)); + //TODO implement var5 contains int speed + return 0; + } + public int MovePlayerToPos(LuaTable var1){ logger.warn("[LUA] Call unchecked MovePlayerToPos with {}", printTable(var1)); //TODO implement var1 contains int[] uid_list, Position pos, int radius, Position rot @@ -1262,6 +1268,7 @@ public class ScriptLib { } public int SetWeatherAreaState(int var1, int var2) { + logger.debug("[LUA] Call SetWeatherAreaState with {} {}", var1, var2); this.getSceneScriptManager().getScene().getPlayers() .forEach(p -> p.setWeather(var1, ClimateType.getTypeByValue(var2))); return 0; @@ -1405,6 +1412,11 @@ public class ScriptLib { return 0; } + public int SetPlayerInteractOption(String var1){ + logger.warn("[LUA] Call unimplemented SetPlayerInteractOption {}", var1); + return 0; + } + public int UnlockForce(int force){ logger.debug("[LUA] Call UnlockForce {}", force); getSceneScriptManager().getScene().unlockForce(force); @@ -1552,7 +1564,7 @@ public class ScriptLib { } public int GetRegionConfigId(LuaTable var1){ - logger.warn("[LUA] Call untested GetRegionConfigId with {}", printTable(var1)); + logger.debug("[LUA] Call GetRegionConfigId with {}", printTable(var1)); var EntityId = var1.get("region_eid").toint(); var entity = getSceneScriptManager().getScene().getScriptManager().getRegionById(EntityId); if (entity == null){ @@ -1657,9 +1669,11 @@ public class ScriptLib { } public int SetGadgetEnableInteract(int groupId, int configId, boolean enable) { - EntityGadget gadget = getCurrentEntityGadget(); - if(gadget.getGroupId() != groupId || gadget.getConfigId() != configId) return -1; - + logger.debug("[LUA] Call SetGadgetEnableInteract with {} {} {}", groupId, configId, enable); + var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, groupId); + if (!(entity instanceof EntityGadget gadget)) { + return -1; + } gadget.setInteractEnabled(enable); return 0; diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java index 253a67e7d..8c8821ca7 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java @@ -7,6 +7,7 @@ import lombok.*; public class SceneGadget extends SceneObject { public int gadget_id; public int chest_drop_id; + public int drop_id; public int drop_count; public String drop_tag; boolean showcutscene; diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java index 77706c7ec..ea4b2ece3 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java @@ -30,16 +30,47 @@ public class SceneRegion { public boolean contains(Position position) { switch (shape) { - case ScriptRegionShape.CUBIC: + case ScriptRegionShape.SPHERE -> { + val x = pos.getX() - position.getX(); + val y = pos.getY() - position.getY(); + val z = pos.getZ() - position.getZ(); + // x^2 + y^2 + z^2 = radius^2 + return x * x + y * y + z * z <= radius * radius; + } + case ScriptRegionShape.CUBIC -> { return (Math.abs(pos.getX() - position.getX()) <= size.getX() / 2f) && (Math.abs(pos.getY() - position.getY()) <= size.getY() / 2f) && (Math.abs(pos.getZ() - position.getZ()) <= size.getZ() / 2f); - case ScriptRegionShape.SPHERE: - var x = Math.pow(pos.getX() - position.getX(), 2); - var y = Math.pow(pos.getY() - position.getY(), 2); - var z = Math.pow(pos.getZ() - position.getZ(), 2); - // ^ means XOR in java! - return x + y + z <= (radius * radius); + } + case ScriptRegionShape.POLYGON -> { + // algorithm is "ray casting": https://www.youtube.com/watch?v=RSXM9bgqxJM + if (Math.abs(pos.getY() - position.getY()) > height / 2f) return false; + var count = 0; + for (var i = 0; i < point_array.size(); ++i) { + val j = (i + 1) % point_array.size(); + + val yp = position.getZ(); + val y1 = point_array.get(i).getY(); + val y2 = point_array.get(j).getY(); + + val xp = position.getX(); + val x1 = point_array.get(i).getX(); + val x2 = point_array.get(j).getX(); + + if ((yp < y1) != (yp < y2) && xp < x1 + ((yp - y1) / (y2 - y1)) * (x2 - x1)) { + ++count; + } + } + + return count % 2 == 1; + } + case ScriptRegionShape.CYLINDER -> { + if (Math.abs(pos.getY() - position.getY()) > height / 2f) return false; + val x = pos.getX() - position.getX(); + val z = pos.getZ() - position.getZ(); + // x^2 + z^2 = radius^2 + return x * x + z * z <= radius * radius; + } } return false; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java new file mode 100644 index 000000000..6b616795b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java @@ -0,0 +1,29 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeAvatarRewardEventGetReqOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketHomeAvatarAllFinishRewardNotify; +import emu.grasscutter.server.packet.send.PacketHomeAvatarRewardEventGetRsp; +import emu.grasscutter.server.packet.send.PacketHomeAvatarRewardEventNotify; + +@Opcodes(PacketOpcodes.HomeAvatarRewardEventGetReq) +public class HandlerHomeAvatarRewardEventGetReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = HomeAvatarRewardEventGetReqOuterClass.HomeAvatarRewardEventGetReq.parseFrom(payload); + + var player = session.getPlayer(); + var rewardsOrError = + player.getCurHomeWorld().getModuleManager().claimAvatarRewards(req.getEventId()); + session.send(new PacketHomeAvatarRewardEventNotify(player)); + session.send(new PacketHomeAvatarAllFinishRewardNotify(player)); + + session.send( + rewardsOrError.map( + gameItems -> new PacketHomeAvatarRewardEventGetRsp(req.getEventId(), gameItems), + integer -> new PacketHomeAvatarRewardEventGetRsp(req.getEventId(), integer))); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java new file mode 100644 index 000000000..ada4476b3 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java @@ -0,0 +1,22 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeAvatarSummonEventReqOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonEventRsp; + +@Opcodes(PacketOpcodes.HomeAvatarSummonEventReq) +public class HandlerHomeAvatarSummonEventReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = HomeAvatarSummonEventReqOuterClass.HomeAvatarSummonEventReq.parseFrom(payload); + var moduleManager = session.getPlayer().getCurHomeWorld().getModuleManager(); + var eventOrError = + moduleManager.fireAvatarSummonEvent( + session.getPlayer(), req.getAvatarId(), req.getGuid(), req.getSuitId()); + session.send( + eventOrError.map(PacketHomeAvatarSummonEventRsp::new, PacketHomeAvatarSummonEventRsp::new)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonFinishReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonFinishReq.java new file mode 100644 index 000000000..4027c041a --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonFinishReq.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeAvatarSummonFinishReqOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonAllEventNotify; +import emu.grasscutter.server.packet.send.PacketHomeAvatarSummonFinishRsp; + +@Opcodes(PacketOpcodes.HomeAvatarSummonFinishReq) +public class HandlerHomeAvatarSummonFinishReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = HomeAvatarSummonFinishReqOuterClass.HomeAvatarSummonFinishReq.parseFrom(payload); + var player = session.getPlayer(); + player.getCurHomeWorld().getModuleManager().onFinishSummonEvent(req.getEventId()); + session.send(new PacketHomeAvatarSummonAllEventNotify(session.getPlayer())); + session.send(new PacketHomeAvatarSummonFinishRsp(req.getEventId())); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeEditModeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeEditModeReq.java index 4d6ae5dfa..3befd5b9e 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeEditModeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeEditModeReq.java @@ -1,5 +1,6 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.game.home.HomeScene; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; @@ -31,14 +32,8 @@ public class HandlerHomeChangeEditModeReq extends PacketHandler { session.send(new PacketHomeComfortInfoNotify(session.getPlayer())); if (!req.getIsEnterEditMode()) { - var scene = session.getPlayer().getScene(); - scene.addEntities( - session - .getPlayer() - .getCurHomeWorld() - .getHome() - .getHomeSceneItem(scene.getId()) - .getAnimals(scene)); + var scene = (HomeScene) session.getPlayer().getScene(); + scene.onLeaveEditMode(); } session.send(new PacketHomeChangeEditModeRsp(req.getIsEnterEditMode())); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java index cff46aab2..e05831d40 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChangeModuleReq.java @@ -1,7 +1,5 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.game.world.Position; -import emu.grasscutter.game.world.Scene; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; @@ -21,7 +19,8 @@ public class HandlerHomeChangeModuleReq extends PacketHandler { HomeChangeModuleReqOuterClass.HomeChangeModuleReq req = HomeChangeModuleReqOuterClass.HomeChangeModuleReq.parseFrom(payload); - if (!session.getPlayer().getCurHomeWorld().getGuests().isEmpty()) { + var homeWorld = session.getPlayer().getCurHomeWorld(); + if (!homeWorld.getGuests().isEmpty()) { session.send(new PacketHomeChangeModuleRsp()); return; } @@ -33,13 +32,10 @@ public class HandlerHomeChangeModuleReq extends PacketHandler { session.send(new PacketHomeComfortInfoNotify(session.getPlayer())); int realmId = 2000 + req.getTargetModuleId(); + var scene = homeWorld.getSceneById(realmId); + var pos = scene.getScriptManager().getConfig().born_pos; - Scene scene = session.getPlayer().getWorld().getSceneById(realmId); - Position pos = scene.getScriptManager().getConfig().born_pos; - - session - .getPlayer() - .getWorld() - .transferPlayerToScene(session.getPlayer(), realmId, TeleportType.WAYPOINT, pos); + homeWorld.transferPlayerToScene(session.getPlayer(), realmId, TeleportType.WAYPOINT, pos); + homeWorld.refreshModuleManager(); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeEnterEditModeFinishReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeEnterEditModeFinishReq.java index 7890726e0..9f4f8f5ae 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeEnterEditModeFinishReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeEnterEditModeFinishReq.java @@ -1,10 +1,9 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.game.entity.EntityHomeAnimal; +import emu.grasscutter.game.home.HomeScene; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.VisionTypeOuterClass; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketHomeEnterEditModeFinishRsp; @@ -17,12 +16,8 @@ public class HandlerHomeEnterEditModeFinishReq extends PacketHandler { * This packet is about the edit mode */ - var scene = session.getPlayer().getScene(); - scene.removeEntities( - scene.getEntities().values().stream() - .filter(gameEntity -> gameEntity instanceof EntityHomeAnimal) - .toList(), - VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE); + var scene = (HomeScene) session.getPlayer().getScene(); + scene.onEnterEditModeFinish(); session.send(new PacketHomeEnterEditModeFinishRsp()); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java index 2932a663b..4f7e972c7 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeSceneInitFinishReq.java @@ -1,6 +1,8 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.OtherPlayerEnterHomeNotifyOuterClass; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.*; @@ -26,6 +28,12 @@ public class HandlerHomeSceneInitFinishReq extends PacketHandler { } } + curHomeWorld.ifHost( + session.getPlayer(), + player -> { + player.sendPacket(new PacketHomeAvatarRewardEventNotify(player)); + player.sendPacket(new PacketHomeAvatarSummonAllEventNotify(player)); + }); session.send(new PacketHomeMarkPointNotify(session.getPlayer())); session.send(new PacketHomeSceneInitFinishRsp()); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUpdateArrangementInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUpdateArrangementInfoReq.java index 2b93ac718..e605dc091 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUpdateArrangementInfoReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUpdateArrangementInfoReq.java @@ -5,10 +5,7 @@ import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.HomeUpdateArrangementInfoReqOuterClass; import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketHomeAvatarTalkFinishInfoNotify; -import emu.grasscutter.server.packet.send.PacketHomeBasicInfoNotify; -import emu.grasscutter.server.packet.send.PacketHomeMarkPointNotify; -import emu.grasscutter.server.packet.send.PacketHomeUpdateArrangementInfoRsp; +import emu.grasscutter.server.packet.send.*; @Opcodes(PacketOpcodes.HomeUpdateArrangementInfoReq) public class HandlerHomeUpdateArrangementInfoReq extends PacketHandler { @@ -22,14 +19,17 @@ public class HandlerHomeUpdateArrangementInfoReq extends PacketHandler { session.getPlayer().getHome().getHomeSceneItem(session.getPlayer().getSceneId()); var roomSceneId = homeScene.getRoomSceneId(); - homeScene.update(req.getSceneArrangementInfo()); + homeScene.update(req.getSceneArrangementInfo(), session.getPlayer()); if (roomSceneId != homeScene.getRoomSceneId()) { session.getPlayer().getHome().onMainHouseChanged(); } + session.getPlayer().getCurHomeWorld().getModuleManager().onUpdateArrangement(); + session.send(new PacketHomeAvatarRewardEventNotify(session.getPlayer())); session.send( new PacketHomeBasicInfoNotify(session.getPlayer(), session.getPlayer().isInEditMode())); session.send(new PacketHomeAvatarTalkFinishInfoNotify(session.getPlayer())); + session.send(new PacketHomeAvatarSummonAllEventNotify(session.getPlayer())); session.send(new PacketHomeMarkPointNotify(session.getPlayer())); session.getPlayer().getHome().save(); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java index 15b73fcf0..23db1bfb8 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerItemGivingReq.java @@ -2,6 +2,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; +import emu.grasscutter.game.quest.enums.QuestCond; import emu.grasscutter.game.quest.enums.QuestContent; import emu.grasscutter.net.packet.*; import emu.grasscutter.net.proto.ItemGivingReqOuterClass.ItemGivingReq; @@ -50,8 +51,9 @@ public final class HandlerItemGivingReq extends PacketHandler { player.sendPacket(new PacketItemGivingRsp(giveId, Mode.EXACT_SUCCESS)); // Remove the action from the active givings. questManager.removeGivingItemAction(giveId); - // Queue the content action. + // Queue the content and condition actions. questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, 0); + questManager.queueEvent(QuestCond.QUEST_COND_ITEM_GIVING_FINISHED, giveId, 0); } case GIVING_METHOD_VAGUE_GROUP -> { var matchedGroups = new ArrayList (); @@ -96,8 +98,11 @@ public final class HandlerItemGivingReq extends PacketHandler { player.sendPacket(new PacketItemGivingRsp(matchedGroups.get(0), Mode.GROUP_SUCCESS)); // Mark the giving action as completed. questManager.markCompleted(giveId); - // Queue the content action. - questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, 0); + // Queue the content and condition actions. + questManager.queueEvent( + QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, matchedGroups.get(0)); + questManager.queueEvent( + QuestCond.QUEST_COND_ITEM_GIVING_FINISHED, giveId, matchedGroups.get(0)); } } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java new file mode 100644 index 000000000..df07429f9 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeAvatarAllFinishRewardNotifyOuterClass; + +public class PacketHomeAvatarAllFinishRewardNotify extends BasePacket { + public PacketHomeAvatarAllFinishRewardNotify(Player player) { + super(PacketOpcodes.HomeAvatarAllFinishRewardNotify); + + var list = player.getHome().getFinishedRewardEventIdSet(); + if (list != null) { + this.setData( + HomeAvatarAllFinishRewardNotifyOuterClass.HomeAvatarAllFinishRewardNotify.newBuilder() + .addAllEventIdList(player.getHome().getFinishedRewardEventIdSet())); + } + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java new file mode 100644 index 000000000..8bff115ec --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java @@ -0,0 +1,27 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeAvatarRewardEventGetRspOuterClass; +import java.util.List; + +public class PacketHomeAvatarRewardEventGetRsp extends BasePacket { + public PacketHomeAvatarRewardEventGetRsp(int eventId, List rewards) { + super(PacketOpcodes.HomeAvatarRewardEventGetRsp); + + this.setData( + HomeAvatarRewardEventGetRspOuterClass.HomeAvatarRewardEventGetRsp.newBuilder() + .setEventId(eventId) + .addAllItemList(rewards.stream().map(GameItem::toItemParam).toList())); + } + + public PacketHomeAvatarRewardEventGetRsp(int eventId, int retcode) { + super(PacketOpcodes.HomeAvatarRewardEventGetRsp); + + this.setData( + HomeAvatarRewardEventGetRspOuterClass.HomeAvatarRewardEventGetRsp.newBuilder() + .setEventId(eventId) + .setRetcode(retcode)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventNotify.java new file mode 100644 index 000000000..cea81c2ae --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventNotify.java @@ -0,0 +1,12 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; + +public class PacketHomeAvatarRewardEventNotify extends BasePacket { + public PacketHomeAvatarRewardEventNotify(Player homeOwner) { + super(PacketOpcodes.HomeAvatarRewardEventNotify); + this.setData(homeOwner.getCurHomeWorld().getModuleManager().toRewardEventProto()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonAllEventNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonAllEventNotify.java new file mode 100644 index 000000000..70925d38c --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonAllEventNotify.java @@ -0,0 +1,12 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; + +public class PacketHomeAvatarSummonAllEventNotify extends BasePacket { + public PacketHomeAvatarSummonAllEventNotify(Player homeOwner) { + super(PacketOpcodes.HomeAvatarSummonAllEventNotify); + this.setData(homeOwner.getCurHomeWorld().getModuleManager().toSummonEventProto()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java new file mode 100644 index 000000000..713e45574 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java @@ -0,0 +1,24 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.home.suite.event.HomeAvatarSummonEvent; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeAvatarSummonEventRspOuterClass; + +public class PacketHomeAvatarSummonEventRsp extends BasePacket { + public PacketHomeAvatarSummonEventRsp(HomeAvatarSummonEvent event) { + super(PacketOpcodes.HomeAvatarSummonEventRsp); + + this.setData( + HomeAvatarSummonEventRspOuterClass.HomeAvatarSummonEventRsp.newBuilder() + .setEventId(event.getEventId())); + } + + public PacketHomeAvatarSummonEventRsp(int retcode) { + super(PacketOpcodes.HomeAvatarSummonEventRsp); + + this.setData( + HomeAvatarSummonEventRspOuterClass.HomeAvatarSummonEventRsp.newBuilder() + .setRetcode(retcode)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java new file mode 100644 index 000000000..47be3d18c --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java @@ -0,0 +1,15 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeAvatarSummonFinishRspOuterClass; + +public class PacketHomeAvatarSummonFinishRsp extends BasePacket { + public PacketHomeAvatarSummonFinishRsp(int eventId) { + super(PacketOpcodes.HomeAvatarSummonFinishRsp); + + this.setData( + HomeAvatarSummonFinishRspOuterClass.HomeAvatarSummonFinishRsp.newBuilder() + .setEventId(eventId)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java new file mode 100644 index 000000000..be2a61763 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java @@ -0,0 +1,15 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.PlayerQuitFromHomeNotifyOuterClass; + +public class PacketPlayerQuitFromHomeNotify extends BasePacket { + public PacketPlayerQuitFromHomeNotify( + PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.QuitReason reason) { + super(PacketOpcodes.PlayerQuitFromHomeNotify); + + this.setData( + PlayerQuitFromHomeNotifyOuterClass.PlayerQuitFromHomeNotify.newBuilder().setReason(reason)); + } +} diff --git a/src/main/java/emu/grasscutter/utils/Either.java b/src/main/java/emu/grasscutter/utils/Either.java new file mode 100644 index 000000000..533340a3c --- /dev/null +++ b/src/main/java/emu/grasscutter/utils/Either.java @@ -0,0 +1,155 @@ +package emu.grasscutter.utils; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; + +public abstract class Either { + private static final class Left extends Either { + private final L value; + + public Left(L value) { + this.value = value; + } + + @Override + public Either mapBoth( + Function super L, ? extends U> f1, Function super R, ? extends V> f2) { + return new Left<>(f1.apply(this.value)); + } + + @Override + public T map(Function super L, ? extends T> l, Function super R, ? extends T> r) { + return l.apply(this.value); + } + + @Override + public Either ifLeft(Consumer super L> consumer) { + consumer.accept(this.value); + return this; + } + + @Override + public Either ifRight(Consumer super R> consumer) { + return this; + } + + @Override + public Optional left() { + return Optional.of(this.value); + } + + @Override + public Optional right() { + return Optional.empty(); + } + + @Override + public String toString() { + return "Left[" + this.value + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Left, ?> left = (Left, ?>) o; + return Objects.equals(value, left.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + } + + private static final class Right extends Either { + private final R value; + + public Right(R value) { + this.value = value; + } + + @Override + public Either mapBoth( + Function super L, ? extends U> f1, Function super R, ? extends V> f2) { + return new Right<>(f2.apply(this.value)); + } + + @Override + public T map(Function super L, ? extends T> l, Function super R, ? extends T> r) { + return r.apply(this.value); + } + + @Override + public Either ifLeft(Consumer super L> consumer) { + return this; + } + + @Override + public Either ifRight(Consumer super R> consumer) { + consumer.accept(this.value); + return this; + } + + @Override + public Optional left() { + return Optional.empty(); + } + + @Override + public Optional right() { + return Optional.of(this.value); + } + + @Override + public String toString() { + return "Right[" + this.value + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Right, ?> right = (Right, ?>) o; + return Objects.equals(value, right.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + } + + private Either() {} + + public abstract Either mapBoth( + Function super L, ? extends U> f1, Function super R, ? extends V> f2); + + public abstract T map(Function super L, ? extends T> l, Function super R, ? extends T> r); + + public abstract Either ifLeft(Consumer super L> consumer); + + public abstract Either ifRight(Consumer super R> consumer); + + public abstract Optional left(); + + public abstract Optional right(); + + public Either mapLeft(Function super L, ? extends T> l) { + return map(t -> left(l.apply(t)), Either::right); + } + + public Either mapRight(Function super R, ? extends T> l) { + return map(Either::left, t -> right(l.apply(t))); + } + + public static Either left(L value) { + return new Left<>(value); + } + + public static Either right(R value) { + return new Right<>(value); + } +} diff --git a/src/main/resources/languages/ru-RU.json b/src/main/resources/languages/ru-RU.json index 9828e1d6d..74f0c6417 100644 --- a/src/main/resources/languages/ru-RU.json +++ b/src/main/resources/languages/ru-RU.json @@ -260,7 +260,7 @@ "refreshed": "Группа %s обновлена." }, "cutscene": { - "description": "Odtwarza przerywnik filmowy" + "description": "Проигрывает кат-сцену" }, "sound": { "description": "Проигрывает звук" @@ -411,7 +411,7 @@ "description": "Генерирует отладочную информацию для решения проблем." }, "debug": { - "description": "🇺🇸Useful debugging commands for developers." + "description": "Полезные отладочные команды для разработкиков." } }, "gacha": { @@ -451,9 +451,9 @@ }, "plugin": { "directory_failed": "Ошибка создания директории плагинов: ", - "unable_to_load": "Невозможно загрудить плагин.", + "unable_to_load": "Невозможно загрузить плагин.", "invalid_config": "Плагин %s имеет недопустимый файл конфигурации.", - "invalid_main_class": "Плагин %s имеет неверный главный класс.", + "invalid_main_class": "Плагин %s имеет некорректный главный класс.", "missing_config": "Плагин %s не имеет корректного файла конфигурации.", "failed_to_load_plugin": "Ошибка загрузки плагина: %s", "failed_to_load": "Не удалось загрузить Плагин.",