diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 10c622904..e486c5655 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -4,33 +4,57 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.data.binout.*; import emu.grasscutter.data.binout.config.*; import emu.grasscutter.data.binout.routes.Route; -import emu.grasscutter.data.custom.*; +import emu.grasscutter.data.custom.TrialAvatarActivityCustomData; +import emu.grasscutter.data.custom.TrialAvatarCustomData; import emu.grasscutter.data.excels.*; -import emu.grasscutter.data.excels.achievement.*; -import emu.grasscutter.data.excels.activity.*; +import emu.grasscutter.data.excels.achievement.AchievementData; +import emu.grasscutter.data.excels.achievement.AchievementGoalData; +import emu.grasscutter.data.excels.activity.ActivityCondExcelConfigData; +import emu.grasscutter.data.excels.activity.ActivityData; +import emu.grasscutter.data.excels.activity.ActivityShopData; +import emu.grasscutter.data.excels.activity.ActivityWatcherData; import emu.grasscutter.data.excels.avatar.*; import emu.grasscutter.data.excels.codex.*; import emu.grasscutter.data.excels.dungeon.*; -import emu.grasscutter.data.excels.giving.*; -import emu.grasscutter.data.excels.monster.*; -import emu.grasscutter.data.excels.quest.*; -import emu.grasscutter.data.excels.reliquary.*; +import emu.grasscutter.data.excels.giving.GivingData; +import emu.grasscutter.data.excels.giving.GivingGroupData; +import emu.grasscutter.data.excels.monster.MonsterCurveData; +import emu.grasscutter.data.excels.monster.MonsterData; +import emu.grasscutter.data.excels.monster.MonsterDescribeData; +import emu.grasscutter.data.excels.monster.MonsterSpecialNameData; +import emu.grasscutter.data.excels.quest.QuestData; +import emu.grasscutter.data.excels.quest.QuestGlobalVarData; +import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData; +import emu.grasscutter.data.excels.reliquary.ReliquaryLevelData; +import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData; +import emu.grasscutter.data.excels.reliquary.ReliquarySetData; import emu.grasscutter.data.excels.scene.*; -import emu.grasscutter.data.excels.tower.*; +import emu.grasscutter.data.excels.tower.TowerFloorData; +import emu.grasscutter.data.excels.tower.TowerLevelData; +import emu.grasscutter.data.excels.tower.TowerScheduleData; import emu.grasscutter.data.excels.trial.*; -import emu.grasscutter.data.excels.weapon.*; -import emu.grasscutter.data.excels.world.*; +import emu.grasscutter.data.excels.weapon.WeaponCurveData; +import emu.grasscutter.data.excels.weapon.WeaponLevelData; +import emu.grasscutter.data.excels.weapon.WeaponPromoteData; +import emu.grasscutter.data.excels.world.WeatherData; +import emu.grasscutter.data.excels.world.WorldAreaData; +import emu.grasscutter.data.excels.world.WorldLevelData; import emu.grasscutter.data.server.*; import emu.grasscutter.game.dungeons.DungeonDropEntry; -import emu.grasscutter.game.quest.*; +import emu.grasscutter.game.quest.QuestEncryptionKey; +import emu.grasscutter.game.quest.RewindData; +import emu.grasscutter.game.quest.TeleportData; import emu.grasscutter.game.quest.enums.QuestCond; import emu.grasscutter.game.world.GroupReplacementData; import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.*; +import lombok.Getter; +import lombok.Setter; +import lombok.val; + +import javax.annotation.Nullable; import java.lang.reflect.Field; import java.util.*; -import javax.annotation.Nullable; -import lombok.*; @SuppressWarnings({"unused", "MismatchedQueryAndUpdateOfCollection"}) public final class GameData { @@ -265,6 +289,10 @@ public final class GameData { private static final Int2ObjectMap homeWorldLevelDataMap = new Int2ObjectOpenHashMap<>(); + @Getter + private static final Int2ObjectMap homeWorldNPCDataMap = + new Int2ObjectOpenHashMap<>(); + @Getter private static final Int2ObjectMap investigationMonsterDataMap = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/emu/grasscutter/data/excels/HomeWorldNPCData.java b/src/main/java/emu/grasscutter/data/excels/HomeWorldNPCData.java new file mode 100644 index 000000000..6402d93f1 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/HomeWorldNPCData.java @@ -0,0 +1,35 @@ +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.inventory.ItemQuality; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +import java.util.List; + +@ResourceType(name = "HomeWorldNPCExcelConfigData.json") +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +public class HomeWorldNPCData extends GameResource { + int furnitureID; + int avatarID; + @SerializedName(value = "npcId", alternate = {"HDLJMOGHICL"}) + int npcId; + @SerializedName(value = "talkIdList", alternate = {"CKMCLCNIBLD"}) + List talkIdList; + @SerializedName(value = "isTalkRandomly", alternate = {"HPJMMEBNMAI"}) + boolean isTalkRandomly; + @SerializedName(value = "npcQuality", alternate = {"BHJOIKFHIBD"}) + ItemQuality npcQuality; + @SerializedName(value = "titleTextMapHash", alternate = {"GNMAIEGCFPO"}) + long titleTextMapHash; + long descTextMapHash; + + @Override + public int getId() { + return this.avatarID; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/ItemData.java b/src/main/java/emu/grasscutter/data/excels/ItemData.java index 9ae04d278..1a10152a7 100644 --- a/src/main/java/emu/grasscutter/data/excels/ItemData.java +++ b/src/main/java/emu/grasscutter/data/excels/ItemData.java @@ -4,6 +4,7 @@ import com.google.gson.annotations.SerializedName; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.common.ItemUseData; +import emu.grasscutter.game.home.SpecialFurnitureType; import emu.grasscutter.game.inventory.EquipType; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.inventory.MaterialType; @@ -13,10 +14,11 @@ import emu.grasscutter.game.props.ItemUseOp; import emu.grasscutter.game.props.ItemUseTarget; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; +import lombok.Getter; + import java.util.Arrays; import java.util.List; import java.util.Objects; -import lombok.Getter; @ResourceType( name = { @@ -86,6 +88,7 @@ public class ItemData extends GameResource { private int comfort; private List furnType; private List furnitureGadgetID; + private SpecialFurnitureType specialFurnitureType = SpecialFurnitureType.NOT_SPECIAL; @SerializedName( value = "roomSceneId", diff --git a/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java b/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java index 22b0c6cf6..e2b1f0d8b 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeBlockItem.java @@ -4,12 +4,15 @@ import dev.morphia.annotations.Entity; import dev.morphia.annotations.Id; import emu.grasscutter.data.binout.HomeworldDefaultSaveData; import emu.grasscutter.net.proto.HomeBlockArrangementInfoOuterClass.HomeBlockArrangementInfo; -import java.util.List; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; import lombok.experimental.FieldDefaults; +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; + @Entity @Data @Builder(builderMethodName = "of") @@ -77,6 +80,8 @@ public class HomeBlockItem { .setIsUnlocked(unlocked) .setComfortValue(calComfort()); + this.reassignIfNull(); + this.deployFurnitureList.forEach(f -> proto.addDeployFurniureList(f.toProto())); this.persistentFurnitureList.forEach(f -> proto.addPersistentFurnitureList(f.toProto())); this.deployAnimalList.forEach(f -> proto.addDeployAnimalList(f.toProto())); @@ -84,4 +89,26 @@ public class HomeBlockItem { return proto.build(); } + + // TODO add more types (farm field and suite) + public List getMarkPointProtoFactories() { + this.reassignIfNull(); + + return Stream.of(this.deployFurnitureList, this.persistentFurnitureList, this.deployNPCList).flatMap(Collection::stream).toList(); + } + + public void reassignIfNull() { + if (this.deployFurnitureList == null) { + this.deployFurnitureList = List.of(); + } + if (this.persistentFurnitureList == null) { + this.persistentFurnitureList = List.of(); + } + if (this.deployAnimalList == null) { + this.deployAnimalList = List.of(); + } + if (this.deployNPCList == null) { + this.deployNPCList = List.of(); + } + } } diff --git a/src/main/java/emu/grasscutter/game/home/HomeFurnitureItem.java b/src/main/java/emu/grasscutter/game/home/HomeFurnitureItem.java index adaf1b17b..11752f5e2 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeFurnitureItem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeFurnitureItem.java @@ -11,12 +11,24 @@ import lombok.AccessLevel; import lombok.Builder; import lombok.Data; import lombok.experimental.FieldDefaults; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; +import java.util.stream.Collectors; @Entity @Data @FieldDefaults(level = AccessLevel.PRIVATE) @Builder(builderMethodName = "of") -public class HomeFurnitureItem { +public class HomeFurnitureItem implements HomeMarkPointProtoFactory { + public static final int PAIMON_FURNITURE_ID = 368134; + public static final int TELEPORT_FURNITURE_ID = 373501; + public static final Set APARTMENT_FURNITURE_ID_SET = GameData.getItemDataMap().values() + .stream() + .filter(itemData -> itemData.getSpecialFurnitureType() == SpecialFurnitureType.Apartment) + .map(ItemData::getId) + .collect(Collectors.toUnmodifiableSet()); + int furnitureId; int guid; int parentFurnitureIndex; @@ -56,17 +68,6 @@ public class HomeFurnitureItem { .build(); } - public HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData toMarkPointProto( - int type) { - return HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData.newBuilder() - .setFurnitureId(furnitureId) - .setGuid(guid) - .setFurnitureType(type) - .setPos(spawnPos.toProto()) - // TODO NPC and farm - .build(); - } - public ItemData getAsItem() { return GameData.getItemDataMap().get(this.furnitureId); } @@ -79,4 +80,29 @@ public class HomeFurnitureItem { } return item.getComfort(); } + + @Nullable + @Override + public HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData toMarkPointProto() { + var type = this.adjustByFurnitureId(); + if (type == SpecialFurnitureType.NOT_SPECIAL) { + return null; + } + + return HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData.newBuilder() + .setFurnitureId(this.furnitureId) + .setFurnitureType(type.getValue()) + .setPos(this.spawnPos.toProto()) + .setGuid(this.guid) + .build(); + } + + @Override + public SpecialFurnitureType adjustByFurnitureId() { + return switch (this.furnitureId) { + case PAIMON_FURNITURE_ID -> SpecialFurnitureType.Paimon; + case TELEPORT_FURNITURE_ID -> SpecialFurnitureType.TeleportPoint; + default -> APARTMENT_FURNITURE_ID_SET.contains(this.furnitureId) ? SpecialFurnitureType.Apartment : SpecialFurnitureType.NOT_SPECIAL; + }; + } } diff --git a/src/main/java/emu/grasscutter/game/home/HomeMarkPointProtoFactory.java b/src/main/java/emu/grasscutter/game/home/HomeMarkPointProtoFactory.java new file mode 100644 index 000000000..a1b7b33df --- /dev/null +++ b/src/main/java/emu/grasscutter/game/home/HomeMarkPointProtoFactory.java @@ -0,0 +1,21 @@ +package emu.grasscutter.game.home; + +import emu.grasscutter.net.proto.HomeMarkPointFurnitureDataOuterClass; +import org.jetbrains.annotations.Nullable; + +public interface HomeMarkPointProtoFactory { + @Nullable + HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData toMarkPointProto(); + + default SpecialFurnitureType adjustByFurnitureId() { + return this.getType(); + } + + default SpecialFurnitureType getType() { + return SpecialFurnitureType.NOT_SPECIAL; + } + + default boolean isProtoConvertible() { + return this.adjustByFurnitureId() != SpecialFurnitureType.NOT_SPECIAL; + } +} diff --git a/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java b/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java index cce6fa055..129b60248 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeNPCItem.java @@ -1,18 +1,23 @@ package emu.grasscutter.game.home; import dev.morphia.annotations.Entity; +import emu.grasscutter.data.GameData; import emu.grasscutter.game.world.Position; +import emu.grasscutter.net.proto.HomeMarkPointFurnitureDataOuterClass; +import emu.grasscutter.net.proto.HomeMarkPointNPCDataOuterClass; import emu.grasscutter.net.proto.HomeNpcDataOuterClass; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; import lombok.experimental.FieldDefaults; +import org.jetbrains.annotations.Nullable; @Entity @Data @FieldDefaults(level = AccessLevel.PRIVATE) @Builder(builderMethodName = "of") -public class HomeNPCItem { +public class HomeNPCItem implements HomeMarkPointProtoFactory { + transient int furnitureId; int avatarId; Position spawnPos; Position spawnRot; @@ -20,19 +25,52 @@ public class HomeNPCItem { public static HomeNPCItem parseFrom(HomeNpcDataOuterClass.HomeNpcData homeNpcData) { return HomeNPCItem.of() - .avatarId(homeNpcData.getAvatarId()) - .spawnPos(new Position(homeNpcData.getSpawnPos())) - .spawnRot(new Position(homeNpcData.getSpawnRot())) - .costumeId(homeNpcData.getCostumeId()) - .build(); + .avatarId(homeNpcData.getAvatarId()) + .spawnPos(new Position(homeNpcData.getSpawnPos())) + .spawnRot(new Position(homeNpcData.getSpawnRot())) + .costumeId(homeNpcData.getCostumeId()) + .build(); } public HomeNpcDataOuterClass.HomeNpcData toProto() { return HomeNpcDataOuterClass.HomeNpcData.newBuilder() - .setAvatarId(avatarId) - .setSpawnPos(spawnPos.toProto()) - .setSpawnRot(spawnRot.toProto()) - .setCostumeId(costumeId) - .build(); + .setAvatarId(avatarId) + .setSpawnPos(spawnPos.toProto()) + .setSpawnRot(spawnRot.toProto()) + .setCostumeId(costumeId) + .build(); + } + + public int getFurnitureId() { + if (this.furnitureId == 0) { + var data = GameData.getHomeWorldNPCDataMap().get(this.avatarId); + this.furnitureId = data == null ? -1 : data.getFurnitureID(); + } + + return this.furnitureId; + } + + @Nullable + @Override + public HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData toMarkPointProto() { + return HomeMarkPointFurnitureDataOuterClass.HomeMarkPointFurnitureData.newBuilder() + .setFurnitureId(this.getFurnitureId()) + .setFurnitureType(this.getType().getValue()) + .setPos(this.spawnPos.toProto()) + .setNpcData(HomeMarkPointNPCDataOuterClass.HomeMarkPointNPCData.newBuilder() + .setAvatarId(this.avatarId) + .setCostumeId(this.costumeId) + .build()) + .build(); + } + + @Override + public SpecialFurnitureType getType() { + return SpecialFurnitureType.NPC; + } + + @Override + public boolean isProtoConvertible() { + return this.getFurnitureId() > 0; } } diff --git a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java index d8e22dc4b..c3c38ef71 100644 --- a/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java +++ b/src/main/java/emu/grasscutter/game/home/HomeSceneItem.java @@ -6,13 +6,14 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.data.binout.HomeworldDefaultSaveData; import emu.grasscutter.game.world.Position; import emu.grasscutter.net.proto.HomeSceneArrangementInfoOuterClass.HomeSceneArrangementInfo; -import java.util.Map; -import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; import lombok.experimental.FieldDefaults; +import java.util.Map; +import java.util.stream.Collectors; + @Entity @Data @Builder(builderMethodName = "of") @@ -64,12 +65,16 @@ public class HomeSceneItem { } public int getRoomSceneId() { - if (mainHouse == null || mainHouse.getAsItem() == null) { + if (this.isRoom()) { return 0; } return mainHouse.getAsItem().getRoomSceneId(); } + public boolean isRoom() { + return mainHouse == null || mainHouse.getAsItem() == null; + } + public int calComfort() { return this.blockItems.values().stream().mapToInt(HomeBlockItem::calComfort).sum(); } diff --git a/src/main/java/emu/grasscutter/game/home/SpecialFurnitureType.java b/src/main/java/emu/grasscutter/game/home/SpecialFurnitureType.java new file mode 100644 index 000000000..d2368838a --- /dev/null +++ b/src/main/java/emu/grasscutter/game/home/SpecialFurnitureType.java @@ -0,0 +1,20 @@ +package emu.grasscutter.game.home; + +import lombok.Getter; + +@Getter +public enum SpecialFurnitureType { + NOT_SPECIAL(-1), + FarmField(2), + TeleportPoint(3), + NPC(5), + Apartment(6), + FurnitureSuite(7), + Paimon(8); + + private final int value; + + SpecialFurnitureType(int value) { + this.value = value; + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeMarkPointNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeMarkPointNotify.java index 750d981ca..908dabd1b 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeMarkPointNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeMarkPointNotify.java @@ -1,11 +1,13 @@ package emu.grasscutter.server.packet.send; import emu.grasscutter.game.home.HomeBlockItem; +import emu.grasscutter.game.home.HomeMarkPointProtoFactory; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.HomeMarkPointNotifyOuterClass; import emu.grasscutter.net.proto.HomeMarkPointSceneDataOuterClass; +import emu.grasscutter.net.proto.VectorOuterClass; import java.util.Collection; @@ -15,8 +17,9 @@ public class PacketHomeMarkPointNotify extends BasePacket { super(PacketOpcodes.HomeMarkPointNotify); var proto = HomeMarkPointNotifyOuterClass.HomeMarkPointNotify.newBuilder(); - var owner = player.getCurHomeWorld().getHost(); - var home = player.getCurHomeWorld().getHome(); + var world = player.getCurHomeWorld(); + var owner = world.getHost(); + var home = world.getHome(); if (owner.getRealmList() == null) { return; @@ -29,18 +32,15 @@ public class PacketHomeMarkPointNotify extends BasePacket { HomeMarkPointSceneDataOuterClass.HomeMarkPointSceneData.newBuilder() .setModuleId(moduleId) .setSceneId(moduleId + 2000) - .setSafePointPos(homeScene.getBornPos().toProto()) - .setTeapotSpiritPos(homeScene.getDjinnPos().toProto()); + .setSafePointPos(homeScene.isRoom() ? VectorOuterClass.Vector.newBuilder().build() : world.getSceneById(moduleId + 2000).getScriptManager().getConfig().born_pos.toProto()) + .setTeapotSpiritPos(homeScene.isRoom() ? VectorOuterClass.Vector.newBuilder().build() : homeScene.getDjinnPos().toProto()); - // Now it only supports the teleport point - // TODO add more types - var marks = - homeScene.getBlockItems().values().stream() - .map(HomeBlockItem::getDeployFurnitureList) - .flatMap(Collection::stream) - .filter(i -> i.getFurnitureId() == 373501) - .map(x -> x.toMarkPointProto(3)) - .toList(); + var marks = homeScene.getBlockItems().values().stream() + .map(HomeBlockItem::getMarkPointProtoFactories) + .flatMap(Collection::stream) + .filter(HomeMarkPointProtoFactory::isProtoConvertible) + .map(HomeMarkPointProtoFactory::toMarkPointProto) + .toList(); markPointData.addAllFurnitureList(marks); proto.addMarkPointDataList(markPointData);