Commit e16633e3 authored by Akka's avatar Akka Committed by Melledy
Browse files

Implement the Home System (Serenitea Pot)

parent 399275e8
...@@ -76,7 +76,7 @@ dependencies { ...@@ -76,7 +76,7 @@ dependencies {
implementation group: 'org.reflections', name: 'reflections', version: '0.10.2' implementation group: 'org.reflections', name: 'reflections', version: '0.10.2'
implementation group: 'dev.morphia.morphia', name: 'morphia-core', version: '2.2.6' implementation group: 'dev.morphia.morphia', name: 'morphia-core', version: '2.2.7'
implementation group: 'org.greenrobot', name: 'eventbus-java', version: '3.3.1' implementation group: 'org.greenrobot', name: 'eventbus-java', version: '3.3.1'
implementation group: 'org.danilopianini', name: 'java-quadtree', version: '0.1.9' implementation group: 'org.danilopianini', name: 'java-quadtree', version: '0.1.9'
......
...@@ -7,12 +7,8 @@ import java.util.List; ...@@ -7,12 +7,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.data.binout.MainQuestData;
import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.excels.*; import emu.grasscutter.data.excels.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
...@@ -29,6 +25,8 @@ public class GameData { ...@@ -29,6 +25,8 @@ public class GameData {
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>(); private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<HomeworldDefaultSaveData> homeworldDefaultSaveData = new Int2ObjectOpenHashMap<>();
// ExcelConfigs // ExcelConfigs
private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
...@@ -140,7 +138,9 @@ public class GameData { ...@@ -140,7 +138,9 @@ public class GameData {
public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() { public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() {
return mainQuestData; return mainQuestData;
} }
public static Int2ObjectMap<HomeworldDefaultSaveData> getHomeworldDefaultSaveData() {
return homeworldDefaultSaveData;
}
public static Int2ObjectMap<AvatarData> getAvatarDataMap() { public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
return avatarDataMap; return avatarDataMap;
} }
......
package emu.grasscutter.data; package emu.grasscutter.data;
import java.io.*; import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.google.gson.Gson; import com.google.gson.Gson;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import lombok.SneakyThrows;
import org.reflections.Reflections; import org.reflections.Reflections;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
...@@ -15,12 +19,6 @@ import com.google.gson.annotations.SerializedName; ...@@ -15,12 +19,6 @@ import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.data.binout.AbilityModifier;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.data.binout.MainQuestData;
import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData; import emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType;
...@@ -66,6 +64,40 @@ public class ResourceLoader { ...@@ -66,6 +64,40 @@ public class ResourceLoader {
loadQuests(); loadQuests();
// Load scene points - must be done AFTER resources are loaded // Load scene points - must be done AFTER resources are loaded
loadScenePoints(); loadScenePoints();
// Load default home layout
loadHomeworldDefaultSaveData();
// Custom - TODO move this somewhere else
try {
GameData.getAvatarSkillDepotDataMap().get(504).setAbilities(
new AbilityEmbryoEntry(
"",
new String[] {
"Avatar_PlayerBoy_ExtraAttack_Wind",
"Avatar_Player_UziExplode_Mix",
"Avatar_Player_UziExplode",
"Avatar_Player_UziExplode_Strike_01",
"Avatar_Player_UziExplode_Strike_02",
"Avatar_Player_WindBreathe",
"Avatar_Player_WindBreathe_CameraController"
}
));
GameData.getAvatarSkillDepotDataMap().get(704).setAbilities(
new AbilityEmbryoEntry(
"",
new String[] {
"Avatar_PlayerGirl_ExtraAttack_Wind",
"Avatar_Player_UziExplode_Mix",
"Avatar_Player_UziExplode",
"Avatar_Player_UziExplode_Strike_01",
"Avatar_Player_UziExplode_Strike_02",
"Avatar_Player_WindBreathe",
"Avatar_Player_WindBreathe_CameraController"
}
));
} catch (Exception e) {
Grasscutter.getLogger().error("Error loading abilities", e);
}
} }
public static void loadResources() { public static void loadResources() {
...@@ -394,6 +426,26 @@ public class ResourceLoader { ...@@ -394,6 +426,26 @@ public class ResourceLoader {
Grasscutter.getLogger().info("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas."); Grasscutter.getLogger().info("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
} }
@SneakyThrows
private static void loadHomeworldDefaultSaveData(){
var folder = Files.list(Path.of(RESOURCE("BinOutput/HomeworldDefaultSave"))).toList();
var pattern = Pattern.compile("scene(.*)_home_config.json");
for(var file : folder){
var matcher = pattern.matcher(file.getFileName().toString());
if(!matcher.find()){
continue;
}
var sceneId = matcher.group(1);
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), HomeworldDefaultSaveData.class);
GameData.getHomeworldDefaultSaveData().put(Integer.parseInt(sceneId), data);
}
Grasscutter.getLogger().info("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas.");
}
// BinOutput configs // BinOutput configs
public static class AvatarConfig { public static class AvatarConfig {
......
package emu.grasscutter.data.binout;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.utils.Position;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.util.List;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class HomeworldDefaultSaveData {
@SerializedName(value = "KFHBFNPDJBE", alternate = "PKACPHDGGEI")
List<HomeBlock> homeBlockLists;
@SerializedName(value = "IJNPADKGNKE", alternate = "MINCKHBNING")
Position bornPos;
@SerializedName("IPIIGEMFLHK")
Position bornRot;
@SerializedName("HHOLBNPIHEM")
Position djinPos;
@SerializedName("KNHCJKHCOAN")
HomeFurniture mainhouse;
@SerializedName("NIHOJFEKFPG")
List<HomeFurniture> doorLists;
@SerializedName("EPGELGEFJFK")
List<HomeFurniture> stairLists;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class HomeBlock{
@SerializedName(value = "FGIJCELCGFI", alternate = "PGDPDIDJEEL")
int blockId;
@SerializedName(value = "BEAPOFELABD", alternate = "MLIODLGDFHJ")
List<HomeFurniture> furnitures;
}
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class HomeFurniture{
@SerializedName(value = "ENHNGKJBJAB", alternate = "KMAAJJHPNBA")
int id;
@SerializedName(value = "NGIEEIOLPPO", alternate = "JFKAHNCPDME")
Position pos;
//@SerializedName(value = "HEOCEHKEBFM", alternate = "LKCKOOGFDBM")
Position rot;
}
}
...@@ -2,14 +2,20 @@ package emu.grasscutter.data.excels; ...@@ -2,14 +2,20 @@ package emu.grasscutter.data.excels;
import java.util.List; import java.util.List;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemUseData; import emu.grasscutter.data.common.ItemUseData;
import emu.grasscutter.game.inventory.*;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
@ResourceType(name = {"MaterialExcelConfigData.json", "WeaponExcelConfigData.json", "ReliquaryExcelConfigData.json"}) @ResourceType(name = {"MaterialExcelConfigData.json",
"WeaponExcelConfigData.json",
"ReliquaryExcelConfigData.json",
"HomeWorldFurnitureExcelConfigData.json"
})
public class ItemData extends GameResource { public class ItemData extends GameResource {
private int id; private int id;
...@@ -63,12 +69,19 @@ public class ItemData extends GameResource { ...@@ -63,12 +69,19 @@ public class ItemData extends GameResource {
private long nameTextMapHash; private long nameTextMapHash;
// Post load // Post load
private transient emu.grasscutter.game.inventory.MaterialType materialEnumType; private transient MaterialType materialEnumType;
private transient emu.grasscutter.game.inventory.ItemType itemEnumType; private transient ItemType itemEnumType;
private transient emu.grasscutter.game.inventory.EquipType equipEnumType; private transient EquipType equipEnumType;
private IntSet addPropLevelSet; private IntSet addPropLevelSet;
// Furniture
private int comfort;
private List<Integer> furnType;
private List<Integer> furnitureGadgetID;
@SerializedName("JFDLJGDFIGL")
private int roomSceneId;
@Override @Override
public int getId(){ public int getId(){
return this.id; return this.id;
...@@ -194,40 +207,56 @@ public class ItemData extends GameResource { ...@@ -194,40 +207,56 @@ public class ItemData extends GameResource {
return maxLevel; return maxLevel;
} }
public emu.grasscutter.game.inventory.ItemType getItemType() { public int getComfort() {
return comfort;
}
public List<Integer> getFurnType() {
return furnType;
}
public List<Integer> getFurnitureGadgetID() {
return furnitureGadgetID;
}
public int getRoomSceneId() {
return roomSceneId;
}
public ItemType getItemType() {
return this.itemEnumType; return this.itemEnumType;
} }
public emu.grasscutter.game.inventory.MaterialType getMaterialType() { public MaterialType getMaterialType() {
return this.materialEnumType; return this.materialEnumType;
} }
public emu.grasscutter.game.inventory.EquipType getEquipType() { public EquipType getEquipType() {
return this.equipEnumType; return this.equipEnumType;
} }
public boolean canAddRelicProp(int level) { public boolean canAddRelicProp(int level) {
return this.addPropLevelSet != null & this.addPropLevelSet.contains(level); return this.addPropLevelSet != null && this.addPropLevelSet.contains(level);
} }
public boolean isEquip() { public boolean isEquip() {
return this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_RELIQUARY || this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_WEAPON; return this.itemEnumType == ItemType.ITEM_RELIQUARY || this.itemEnumType == ItemType.ITEM_WEAPON;
} }
@Override @Override
public void onLoad() { public void onLoad() {
this.itemEnumType = emu.grasscutter.game.inventory.ItemType.getTypeByName(getItemTypeString()); this.itemEnumType = ItemType.getTypeByName(getItemTypeString());
this.materialEnumType = emu.grasscutter.game.inventory.MaterialType.getTypeByName(getMaterialTypeString()); this.materialEnumType = MaterialType.getTypeByName(getMaterialTypeString());
if (this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_RELIQUARY) { if (this.itemEnumType == ItemType.ITEM_RELIQUARY) {
this.equipEnumType = emu.grasscutter.game.inventory.EquipType.getTypeByName(this.equipType); this.equipEnumType = EquipType.getTypeByName(this.equipType);
if (this.addPropLevels != null && this.addPropLevels.length > 0) { if (this.addPropLevels != null && this.addPropLevels.length > 0) {
this.addPropLevelSet = new IntOpenHashSet(this.addPropLevels); this.addPropLevelSet = new IntOpenHashSet(this.addPropLevels);
} }
} else if (this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_WEAPON) { } else if (this.itemEnumType == ItemType.ITEM_WEAPON) {
this.equipEnumType = emu.grasscutter.game.inventory.EquipType.EQUIP_WEAPON; this.equipEnumType = EquipType.EQUIP_WEAPON;
} else { } else {
this.equipEnumType = emu.grasscutter.game.inventory.EquipType.EQUIP_NONE; this.equipEnumType = EquipType.EQUIP_NONE;
} }
if (this.getWeaponProperties() != null) { if (this.getWeaponProperties() != null) {
...@@ -235,6 +264,13 @@ public class ItemData extends GameResource { ...@@ -235,6 +264,13 @@ public class ItemData extends GameResource {
weaponProperty.onLoad(); weaponProperty.onLoad();
} }
} }
if(this.getFurnType() != null){
this.furnType = this.furnType.stream().filter(x -> x > 0).toList();
}
if(this.getFurnitureGadgetID() != null){
this.furnitureGadgetID = this.furnitureGadgetID.stream().filter(x -> x > 0).toList();
}
} }
public static class WeaponProperty { public static class WeaponProperty {
......
...@@ -13,6 +13,7 @@ import emu.grasscutter.game.Account; ...@@ -13,6 +13,7 @@ import emu.grasscutter.game.Account;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.friends.Friendship; import emu.grasscutter.game.friends.Friendship;
import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.home.GameHome;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
...@@ -295,4 +296,10 @@ public final class DatabaseHelper { ...@@ -295,4 +296,10 @@ public final class DatabaseHelper {
public static boolean deleteQuest(GameMainQuest quest) { public static boolean deleteQuest(GameMainQuest quest) {
return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged(); return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged();
} }
public static GameHome getHomeByUid(int id) {
return DatabaseManager.getGameDatastore().find(GameHome.class).filter(Filters.eq("ownerUid", id)).first();
}
public static void saveHome(GameHome gameHome) {
DatabaseManager.getGameDatastore().save(gameHome);
}
} }
...@@ -16,6 +16,7 @@ import emu.grasscutter.game.Account; ...@@ -16,6 +16,7 @@ import emu.grasscutter.game.Account;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.friends.Friendship; import emu.grasscutter.game.friends.Friendship;
import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.home.GameHome;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
...@@ -30,7 +31,7 @@ public final class DatabaseManager { ...@@ -30,7 +31,7 @@ public final class DatabaseManager {
private static final Class<?>[] mappedClasses = new Class<?>[] { private static final Class<?>[] mappedClasses = new Class<?>[] {
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
GachaRecord.class, Mail.class, GameMainQuest.class GachaRecord.class, Mail.class, GameMainQuest.class, GameHome.class
}; };
public static Datastore getGameDatastore() { public static Datastore getGameDatastore() {
......
package emu.grasscutter.game.home;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.IndexOptions;
import dev.morphia.annotations.Indexed;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.util.concurrent.ConcurrentHashMap;
@Entity(value = "homes", useDiscriminator = false)
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public class GameHome {
@Id
String id;
@Indexed(options = @IndexOptions(unique = true))
long ownerUid;
ConcurrentHashMap<Integer, HomeSceneItem> sceneMap;
public void save(){
DatabaseHelper.saveHome(this);
}
public static GameHome getByUid(Integer uid){
var home = DatabaseHelper.getHomeByUid(uid);
if (home == null) {
home = GameHome.create(uid);
}
return home;
}
public static GameHome create(Integer uid){
return GameHome.of()
.ownerUid(uid)
.sceneMap(new ConcurrentHashMap<>())
.build();
}
public HomeSceneItem getHomeSceneItem(int sceneId) {
return sceneMap.computeIfAbsent(sceneId, e -> {
var defaultItem = GameData.getHomeworldDefaultSaveData().get(sceneId);
if (defaultItem != null){
Grasscutter.getLogger().info("Set player {} home {} to initial setting", ownerUid, sceneId);
return HomeSceneItem.parseFrom(defaultItem, sceneId);
}
return null;
});
}
public void onOwnerLogin(Player player) {
player.getSession().send(new PacketHomeBasicInfoNotify(player, false));
player.getSession().send(new PacketPlayerHomeCompInfoNotify(player));
player.getSession().send(new PacketHomeComfortInfoNotify(player));
player.getSession().send(new PacketFurnitureCurModuleArrangeCountNotify());
player.getSession().send(new PacketUnlockedFurnitureFormulaDataNotify());
}
}
package emu.grasscutter.game.home;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.grasscutter.data.binout.HomeworldDefaultSaveData;
import emu.grasscutter.net.proto.HomeBlockArrangementInfoOuterClass.HomeBlockArrangementInfo;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.util.List;
@Entity
@Data
@Builder(builderMethodName = "of")
@FieldDefaults(level = AccessLevel.PRIVATE)
public class HomeBlockItem {
@Id
int blockId;
boolean unlocked;
List<HomeFurnitureItem> deployFurnitureList;
public void update(HomeBlockArrangementInfo homeBlockArrangementInfo) {
this.blockId = homeBlockArrangementInfo.getBlockId();
this.deployFurnitureList = homeBlockArrangementInfo.getDeployFurniureListList().stream()
.map(HomeFurnitureItem::parseFrom)
.toList();
}
public int calComfort(){
return this.deployFurnitureList.stream()
.mapToInt(HomeFurnitureItem::getComfort)
.sum();
}
public HomeBlockArrangementInfo toProto() {
var proto = HomeBlockArrangementInfo.newBuilder()
.setBlockId(blockId)
.setIsUnlocked(unlocked)
.setComfortValue(calComfort());
this.deployFurnitureList.forEach(f -> proto.addDeployFurniureList(f.toProto()));
return proto.build();
}
public static HomeBlockItem parseFrom(HomeworldDefaultSaveData.HomeBlock homeBlock) {
return HomeBlockItem.of()
.blockId(homeBlock.getBlockId())
.unlocked(homeBlock.getFurnitures() != null)
.deployFurnitureList(
homeBlock.getFurnitures() == null ? List.of() :
homeBlock.getFurnitures().stream()
.map(HomeFurnitureItem::parseFrom)
.toList())
.build();
}
}
package emu.grasscutter.game.home;
import dev.morphia.annotations.Entity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.HomeworldDefaultSaveData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.net.proto.HomeFurnitureDataOuterClass;
import emu.grasscutter.utils.Position;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Entity
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public class HomeFurnitureItem {
int furnitureId;
int guid;
int parentFurnitureIndex;
Position spawnPos;
Position spawnRot;
public HomeFurnitureDataOuterClass.HomeFurnitureData toProto(){
return HomeFurnitureDataOuterClass.HomeFurnitureData.newBuilder()
.setFurnitureId(furnitureId)
.setGuid(guid)
.setParentFurnitureIndex(parentFurnitureIndex)
.setSpawnPos(spawnPos.toProto())
.setSpawnRot(spawnRot.toProto())
.build();
}
public static HomeFurnitureItem parseFrom(HomeFurnitureDataOuterClass.HomeFurnitureData homeFurnitureData) {
return HomeFurnitureItem.of()
.furnitureId(homeFurnitureData.getFurnitureId())
.guid(homeFurnitureData.getGuid())
.parentFurnitureIndex(homeFurnitureData.getParentFurnitureIndex())
.spawnPos(new Position(homeFurnitureData.getSpawnPos()))
.spawnRot(new Position(homeFurnitureData.getSpawnRot()))
.build();
}
public static HomeFurnitureItem parseFrom(HomeworldDefaultSaveData.HomeFurniture homeFurniture) {
return HomeFurnitureItem.of()
.furnitureId(homeFurniture.getId())
.parentFurnitureIndex(1)
.spawnPos(homeFurniture.getPos() == null ? new Position() : homeFurniture.getPos())
.spawnRot(homeFurniture.getRot() == null ? new Position() : homeFurniture.getRot())
.build();
}
public ItemData getAsItem() {
return GameData.getItemDataMap().get(this.furnitureId);
}
public int getComfort() {
var item = getAsItem();
if (item == null){
return 0;
}
return item.getComfort();
}
}
package emu.grasscutter.game.home;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.grasscutter.data.binout.HomeworldDefaultSaveData;
import emu.grasscutter.net.proto.HomeBasicInfoOuterClass.HomeBasicInfo;
import emu.grasscutter.net.proto.HomeSceneArrangementInfoOuterClass.HomeSceneArrangementInfo;
import emu.grasscutter.utils.Position;
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")
@FieldDefaults(level = AccessLevel.PRIVATE)
public class HomeSceneItem {
@Id
int sceneId;
Map<Integer, HomeBlockItem> blockItems;
Position bornPos;
Position bornRot;
Position djinnPos;
HomeFurnitureItem mainHouse;
public static HomeSceneItem parseFrom(HomeworldDefaultSaveData defaultItem, int sceneId) {
return HomeSceneItem.of()
.sceneId(sceneId)
.blockItems(defaultItem.getHomeBlockLists().stream()
.map(HomeBlockItem::parseFrom)
.collect(Collectors.toMap(HomeBlockItem::getBlockId, y -> y)))
.bornPos(defaultItem.getBornPos())
.bornRot(defaultItem.getBornRot() == null ? new Position() : defaultItem.getBornRot())
.djinnPos(defaultItem.getDjinPos() == null ? new Position() : defaultItem.getDjinPos())
.mainHouse(defaultItem.getMainhouse() == null ? null :
HomeFurnitureItem.parseFrom(defaultItem.getMainhouse()))
.build();
}
public void update(HomeSceneArrangementInfo arrangementInfo){
for(var blockItem : arrangementInfo.getBlockArrangementInfoListList()){
var block = this.blockItems.get(blockItem.getBlockId());
if(block == null){
System.out.println(111);
continue;
}
block.update(blockItem);
this.blockItems.put(blockItem.getBlockId(), block);
}
this.bornPos = new Position(arrangementInfo.getBornPos());
this.bornRot = new Position(arrangementInfo.getBornRot());
this.djinnPos = new Position(arrangementInfo.getDjinnPos());
this.mainHouse = HomeFurnitureItem.parseFrom(arrangementInfo.getMainHouse());
}
public int getRoomSceneId(){
if(mainHouse == null || mainHouse.getAsItem() == null){
return 0;
}
return mainHouse.getAsItem().getRoomSceneId();
}
public int calComfort(){
return this.blockItems.values().stream()
.mapToInt(HomeBlockItem::calComfort)
.sum();
}
public HomeSceneArrangementInfo toProto(){
var proto = HomeSceneArrangementInfo.newBuilder();
blockItems.values().forEach(b -> proto.addBlockArrangementInfoList(b.toProto()));
proto.setComfortValue(calComfort())
.setBornPos(bornPos.toProto())
.setBornRot(bornRot.toProto())
.setDjinnPos(djinnPos.toProto())
.setSceneId(sceneId)
.setTmpVersion(1);
if(mainHouse != null){
proto.setMainHouse(mainHouse.toProto());
}
return proto.build();
}
}
...@@ -267,6 +267,8 @@ public class Inventory implements Iterable<GameItem> { ...@@ -267,6 +267,8 @@ public class Inventory implements Iterable<GameItem> {
getPlayer().setMora(player.getMora() + count); getPlayer().setMora(player.getMora() + count);
case 203 -> // Genesis Crystals case 203 -> // Genesis Crystals
getPlayer().setCrystals(player.getCrystals() + count); getPlayer().setCrystals(player.getCrystals() + count);
case 204 -> // Home Coin
getPlayer().setHomeCoin(player.getHomeCoin() + count);
} }
} }
...@@ -280,6 +282,8 @@ public class Inventory implements Iterable<GameItem> { ...@@ -280,6 +282,8 @@ public class Inventory implements Iterable<GameItem> {
return player.getCrystals(); return player.getCrystals();
case 106: // Resin case 106: // Resin
return player.getProperty(PlayerProperty.PROP_PLAYER_RESIN); return player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
case 204: // Home Coin
return player.getHomeCoin();
default: default:
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S
return (item == null) ? 0 : item.getCount(); return (item == null) ? 0 : item.getCount();
...@@ -320,6 +324,8 @@ public class Inventory implements Iterable<GameItem> { ...@@ -320,6 +324,8 @@ public class Inventory implements Iterable<GameItem> {
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity)); player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
case 106 -> // Resin case 106 -> // Resin
player.getResinManager().useResin(cost.getCount() * quantity); player.getResinManager().useResin(cost.getCount() * quantity);
case 204 -> // Home Coin
player.setHomeCoin(player.getHomeCoin() - (cost.getCount() * quantity));
default -> default ->
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity); removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
} }
......
...@@ -14,6 +14,8 @@ import emu.grasscutter.game.avatar.AvatarProfileData; ...@@ -14,6 +14,8 @@ import emu.grasscutter.game.avatar.AvatarProfileData;
import emu.grasscutter.game.avatar.AvatarStorage; import emu.grasscutter.game.avatar.AvatarStorage;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.EntityVehicle; import emu.grasscutter.game.entity.EntityVehicle;
import emu.grasscutter.game.home.GameHome;
import emu.grasscutter.game.managers.DeforestationManager.DeforestationManager;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
...@@ -164,6 +166,7 @@ public class Player { ...@@ -164,6 +166,7 @@ public class Player {
@Transient private ResinManager resinManager; @Transient private ResinManager resinManager;
@Transient private ForgingManager forgingManager; @Transient private ForgingManager forgingManager;
@Transient private DeforestationManager deforestationManager; @Transient private DeforestationManager deforestationManager;
@Transient private GameHome home;
private long springLastUsed; private long springLastUsed;
private HashMap<String, MapMark> mapMarks; private HashMap<String, MapMark> mapMarks;
...@@ -376,7 +379,9 @@ public class Player { ...@@ -376,7 +379,9 @@ public class Player {
public void setCurrentRealmId(Integer currentRealmId) { public void setCurrentRealmId(Integer currentRealmId) {
this.currentRealmId = currentRealmId; this.currentRealmId = currentRealmId;
} }
public GameHome getHome(){
return home;
}
public Position getPos() { public Position getPos() {
return pos; return pos;
} }
...@@ -429,6 +434,14 @@ public class Player { ...@@ -429,6 +434,14 @@ public class Player {
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_MCOIN)); this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_MCOIN));
} }
public int getHomeCoin() {
return this.getProperty(PlayerProperty.PROP_PLAYER_HOME_COIN);
}
public void setHomeCoin(int coin) {
this.setProperty(PlayerProperty.PROP_PLAYER_HOME_COIN, coin);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_HOME_COIN));
}
private int getExpRequired(int level) { private int getExpRequired(int level) {
PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level); PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level);
return levelData != null ? levelData.getExp() : 0; return levelData != null ? levelData.getExp() : 0;
...@@ -1316,14 +1329,16 @@ public class Player { ...@@ -1316,14 +1329,16 @@ public class Player {
session.send(new PacketCodexDataFullNotify(this)); session.send(new PacketCodexDataFullNotify(this));
session.send(new PacketAllWidgetDataNotify(this)); session.send(new PacketAllWidgetDataNotify(this));
session.send(new PacketWidgetGadgetAllDataNotify()); session.send(new PacketWidgetGadgetAllDataNotify());
session.send(new PacketPlayerHomeCompInfoNotify(this));
session.send(new PacketHomeComfortInfoNotify(this));
session.send(new PacketCombineDataNotify(this.unlockedCombines)); session.send(new PacketCombineDataNotify(this.unlockedCombines));
this.forgingManager.sendForgeDataNotify(); this.forgingManager.sendForgeDataNotify();
this.resinManager.onPlayerLogin(); this.resinManager.onPlayerLogin();
getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward. getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.
// Home
home = GameHome.getByUid(getUid());
home.onOwnerLogin(this);
session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world
session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels)); session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels));
session.send(new PacketOpenStateUpdateNotify()); session.send(new PacketOpenStateUpdateNotify());
......
...@@ -414,6 +414,10 @@ public class Scene { ...@@ -414,6 +414,10 @@ public class Scene {
} }
public void onTick() { public void onTick() {
// disable script for home
if (this.getSceneType() == SceneType.SCENE_HOME_WORLD || this.getSceneType() == SceneType.SCENE_HOME_ROOM){
return;
}
if (this.getScriptManager().isInit()) { if (this.getScriptManager().isInit()) {
this.checkBlocks(); this.checkBlocks();
} else { } else {
......
...@@ -270,7 +270,9 @@ public class World implements Iterable<Player> { ...@@ -270,7 +270,9 @@ public class World implements Iterable<Player> {
enterType = EnterType.ENTER_TYPE_GOTO; enterType = EnterType.ENTER_TYPE_GOTO;
} else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) { } else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) {
// Home // Home
enterReason = EnterReason.EnterHome;
enterType = EnterType.ENTER_TYPE_SELF_HOME; enterType = EnterType.ENTER_TYPE_SELF_HOME;
} }
// Teleport packet // Teleport packet
......
...@@ -777,6 +777,9 @@ public class PacketOpcodes { ...@@ -777,6 +777,9 @@ public class PacketOpcodes {
public static final int HomeSceneJumpRsp = 4570; public static final int HomeSceneJumpRsp = 4570;
public static final int HomeTransferReq = 4880; public static final int HomeTransferReq = 4880;
public static final int HomeTransferRsp = 4767; public static final int HomeTransferRsp = 4767;
public static final int HomeUnknown1Notify = 4528;
public static final int HomeUnknown2Req = 4857;
public static final int HomeUnknown2Rsp = 4670;
public static final int HomeUpdateArrangementInfoReq = 4472; public static final int HomeUpdateArrangementInfoReq = 4472;
public static final int HomeUpdateArrangementInfoRsp = 4822; public static final int HomeUpdateArrangementInfoRsp = 4822;
public static final int HomeUpdateFishFarmingInfoReq = 4604; public static final int HomeUpdateFishFarmingInfoReq = 4604;
......
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.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketFurnitureCurModuleArrangeCountNotify;
@Opcodes(PacketOpcodes.GetFurnitureCurModuleArrangeCountReq)
public class HandlerGetFurnitureCurModuleArrangeCountReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.send(new PacketFurnitureCurModuleArrangeCountNotify());
}
}
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.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketPlayerHomeCompInfoNotify;
@Opcodes(PacketOpcodes.GetPlayerHomeCompInfoReq)
public class HandlerGetPlayerHomeCompInfoReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.send(new PacketPlayerHomeCompInfoNotify(session.getPlayer()));
}
}
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.HomeChangeEditModeReqOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.*;
@Opcodes(PacketOpcodes.HomeChangeEditModeReq)
public class HandlerHomeChangeEditModeReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = HomeChangeEditModeReqOuterClass.HomeChangeEditModeReq.parseFrom(payload);
session.send(new PacketHomeUnknown1Notify(req.getIsEnterEditMode()));
session.send(new PacketHomeBasicInfoNotify(session.getPlayer(), req.getIsEnterEditMode()));
session.send(new PacketHomeComfortInfoNotify(session.getPlayer()));
session.send(new PacketHomeChangeEditModeRsp(req.getIsEnterEditMode()));
}
}
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.HomeGetArrangementInfoReqOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketHomeGetArrangementInfoRsp;
@Opcodes(PacketOpcodes.HomeGetArrangementInfoReq)
public class HandlerHomeGetArrangementInfoReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var req = HomeGetArrangementInfoReqOuterClass.HomeGetArrangementInfoReq.parseFrom(payload);
session.send(new PacketHomeGetArrangementInfoRsp(session.getPlayer(), req.getSceneIdListList()));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment