mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-22 11:54:39 +01:00
Implement the Home System (Serenitea Pot)
This commit is contained in:
70
src/main/java/emu/grasscutter/game/home/GameHome.java
Normal file
70
src/main/java/emu/grasscutter/game/home/GameHome.java
Normal file
@@ -0,0 +1,70 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
64
src/main/java/emu/grasscutter/game/home/HomeBlockItem.java
Normal file
64
src/main/java/emu/grasscutter/game/home/HomeBlockItem.java
Normal file
@@ -0,0 +1,64 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
91
src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
Normal file
91
src/main/java/emu/grasscutter/game/home/HomeSceneItem.java
Normal file
@@ -0,0 +1,91 @@
|
||||
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> {
|
||||
getPlayer().setMora(player.getMora() + count);
|
||||
case 203 -> // Genesis Crystals
|
||||
getPlayer().setCrystals(player.getCrystals() + count);
|
||||
case 204 -> // Home Coin
|
||||
getPlayer().setHomeCoin(player.getHomeCoin() + count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +282,8 @@ public class Inventory implements Iterable<GameItem> {
|
||||
return player.getCrystals();
|
||||
case 106: // Resin
|
||||
return player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||
case 204: // Home Coin
|
||||
return player.getHomeCoin();
|
||||
default:
|
||||
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();
|
||||
@@ -320,6 +324,8 @@ public class Inventory implements Iterable<GameItem> {
|
||||
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
|
||||
case 106 -> // Resin
|
||||
player.getResinManager().useResin(cost.getCount() * quantity);
|
||||
case 204 -> // Home Coin
|
||||
player.setHomeCoin(player.getHomeCoin() - (cost.getCount() * quantity));
|
||||
default ->
|
||||
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import emu.grasscutter.game.avatar.AvatarProfileData;
|
||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
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.EntityItem;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
@@ -164,6 +166,7 @@ public class Player {
|
||||
@Transient private ResinManager resinManager;
|
||||
@Transient private ForgingManager forgingManager;
|
||||
@Transient private DeforestationManager deforestationManager;
|
||||
@Transient private GameHome home;
|
||||
|
||||
private long springLastUsed;
|
||||
private HashMap<String, MapMark> mapMarks;
|
||||
@@ -376,7 +379,9 @@ public class Player {
|
||||
public void setCurrentRealmId(Integer currentRealmId) {
|
||||
this.currentRealmId = currentRealmId;
|
||||
}
|
||||
|
||||
public GameHome getHome(){
|
||||
return home;
|
||||
}
|
||||
public Position getPos() {
|
||||
return pos;
|
||||
}
|
||||
@@ -429,6 +434,14 @@ public class Player {
|
||||
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) {
|
||||
PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level);
|
||||
return levelData != null ? levelData.getExp() : 0;
|
||||
@@ -1316,14 +1329,16 @@ public class Player {
|
||||
session.send(new PacketCodexDataFullNotify(this));
|
||||
session.send(new PacketAllWidgetDataNotify(this));
|
||||
session.send(new PacketWidgetGadgetAllDataNotify());
|
||||
session.send(new PacketPlayerHomeCompInfoNotify(this));
|
||||
session.send(new PacketHomeComfortInfoNotify(this));
|
||||
session.send(new PacketCombineDataNotify(this.unlockedCombines));
|
||||
this.forgingManager.sendForgeDataNotify();
|
||||
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.
|
||||
|
||||
// Home
|
||||
home = GameHome.getByUid(getUid());
|
||||
home.onOwnerLogin(this);
|
||||
|
||||
session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world
|
||||
session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels));
|
||||
session.send(new PacketOpenStateUpdateNotify());
|
||||
|
||||
@@ -414,6 +414,10 @@ public class Scene {
|
||||
}
|
||||
|
||||
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()) {
|
||||
this.checkBlocks();
|
||||
} else {
|
||||
|
||||
@@ -270,7 +270,9 @@ public class World implements Iterable<Player> {
|
||||
enterType = EnterType.ENTER_TYPE_GOTO;
|
||||
} else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) {
|
||||
// Home
|
||||
enterReason = EnterReason.EnterHome;
|
||||
enterType = EnterType.ENTER_TYPE_SELF_HOME;
|
||||
|
||||
}
|
||||
|
||||
// Teleport packet
|
||||
|
||||
Reference in New Issue
Block a user