diff --git a/src/generated/main/java/emu/grasscutter/net/proto/HomePlantSubFieldDataOuterClass.java b/src/generated/main/java/emu/grasscutter/net/proto/HomePlantSubFieldDataOuterClass.java
index e41b29c80..5e5a599f8 100644
--- a/src/generated/main/java/emu/grasscutter/net/proto/HomePlantSubFieldDataOuterClass.java
+++ b/src/generated/main/java/emu/grasscutter/net/proto/HomePlantSubFieldDataOuterClass.java
@@ -36,21 +36,21 @@ public final class HomePlantSubFieldDataOuterClass {
int getEntityIdList(int index);
/**
- * .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 Int2ObjectMap homeWorldBgmDataMap =
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..d5977c817
--- /dev/null
+++ b/src/main/java/emu/grasscutter/data/excels/HomeWorldEventData.java
@@ -0,0 +1,25 @@
+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..c6652d967 100644
--- a/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
+++ b/src/main/java/emu/grasscutter/game/entity/EntityHomeAnimal.java
@@ -10,12 +10,14 @@ import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
import lombok.Getter;
+import java.util.concurrent.atomic.AtomicBoolean;
+
public class EntityHomeAnimal extends EntityMonster implements Rebornable {
private int rebornCDTickCount;
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 +62,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 +76,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..fd5bcc893 100644
--- a/src/main/java/emu/grasscutter/game/home/GameHome.java
+++ b/src/main/java/emu/grasscutter/game/home/GameHome.java
@@ -12,15 +12,17 @@ import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.net.proto.HomeAvatarTalkFinishInfoOuterClass;
import emu.grasscutter.server.packet.send.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.FieldDefaults;
+
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
-import lombok.AccessLevel;
-import lombok.Builder;
-import lombok.Data;
-import lombok.experimental.FieldDefaults;
+import java.util.stream.Stream;
@Entity(value = "homes", useDiscriminator = false)
@Data
@@ -55,6 +57,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 +65,9 @@ public class GameHome {
home = GameHome.create(uid);
}
+ home.reassignIfNull();
home.fixMainHouseIfOld();
+ home.syncHomeAvatarCostume();
return home;
}
@@ -79,9 +84,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 +112,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 +140,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 +176,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 +189,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 +257,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 +449,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 +461,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..3fa916e78 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,8 +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();
}
public int calComfort() {
@@ -81,15 +89,16 @@ 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 +116,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..d49f5650f
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/HomeModuleManager.java
@@ -0,0 +1,204 @@
+package emu.grasscutter.game.home;
+
+import com.github.davidmoten.guavamini.Lists;
+import emu.grasscutter.Grasscutter;
+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 lombok.AccessLevel;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+import java.util.*;
+import java.util.stream.Stream;
+
+@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() {
+ this.outdoor.onTick();
+ this.indoor.onTick();
+ this.summonEvents.removeIf(HomeAvatarSummonEvent::isTimeOver);
+ }
+
+ public void refreshMainHouse() {
+ 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)
+ .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() {
+ 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..3c6df4b22 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..21da833dd 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
@@ -6,17 +6,20 @@ 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 lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.FieldDefaults;
+
+import javax.annotation.Nullable;
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.experimental.FieldDefaults;
@Entity
@Data
@@ -49,14 +52,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 +87,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..8f871657b 100644
--- a/src/main/java/emu/grasscutter/game/home/HomeWorld.java
+++ b/src/main/java/emu/grasscutter/game/home/HomeWorld.java
@@ -8,33 +8,57 @@ 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 java.util.List;
+import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
+import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
+import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify;
import lombok.Getter;
+import java.util.List;
+import java.util.function.Consumer;
+
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() {
+ 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 +212,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..f0da0bf86 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,7 @@ 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 +261,7 @@ 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..263240452
--- /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 lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Objects;
+
+@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..60a4707f9
--- /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 lombok.AccessLevel;
+import lombok.Getter;
+import lombok.experimental.FieldDefaults;
+
+import java.util.List;
+import java.util.Objects;
+
+@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..651212142
--- /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..961cfe7ab
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/HomeAvatarSummonEvent.java
@@ -0,0 +1,36 @@
+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..8c501f462
--- /dev/null
+++ b/src/main/java/emu/grasscutter/game/home/suite/event/SuiteEventType.java
@@ -0,0 +1,19 @@
+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/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/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
new file mode 100644
index 000000000..6a5482edf
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarRewardEventGetReq.java
@@ -0,0 +1,30 @@
+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..c650e0110
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeAvatarSummonEventReq.java
@@ -0,0 +1,19 @@
+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..f18e5c141 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;
@@ -19,9 +17,10 @@ public class HandlerHomeChangeModuleReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
HomeChangeModuleReqOuterClass.HomeChangeModuleReq req =
- HomeChangeModuleReqOuterClass.HomeChangeModuleReq.parseFrom(payload);
+ 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..a86e8f32c 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.*;
@@ -16,16 +18,20 @@ public class HandlerHomeSceneInitFinishReq extends PacketHandler {
session.getPlayer().setHasSentInitPacketInHome(true);
if (curHomeWorld.getHost().isOnline()
- && !curHomeWorld.getHost().equals(session.getPlayer())) {
+ && !curHomeWorld.getHost().equals(session.getPlayer())) {
curHomeWorld
- .getHost()
- .sendPacket(
- new PacketOtherPlayerEnterOrLeaveHomeNotify(
- session.getPlayer(),
- OtherPlayerEnterHomeNotifyOuterClass.OtherPlayerEnterHomeNotify.Reason.ENTER));
+ .getHost()
+ .sendPacket(
+ new PacketOtherPlayerEnterOrLeaveHomeNotify(
+ session.getPlayer(),
+ OtherPlayerEnterHomeNotifyOuterClass.OtherPlayerEnterHomeNotify.Reason.ENTER));
}
}
+ 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/send/PacketHomeAvatarAllFinishRewardNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
new file mode 100644
index 000000000..eda4ae116
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarAllFinishRewardNotify.java
@@ -0,0 +1,18 @@
+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..73aa4d762
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarRewardEventGetRsp.java
@@ -0,0 +1,26 @@
+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..6c28fb695
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonEventRsp.java
@@ -0,0 +1,22 @@
+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..66ad749e4
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeAvatarSummonFinishRsp.java
@@ -0,0 +1,14 @@
+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..2c5a82332
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerQuitFromHomeNotify.java
@@ -0,0 +1,14 @@
+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..b976fdfbd
--- /dev/null
+++ b/src/main/java/emu/grasscutter/utils/Either.java
@@ -0,0 +1,153 @@
+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);
+ }
+}