mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2024-11-23 12:29:52 +00:00
Levelup City Implementation (#2281)
* add statue promo data * implement levelup city feature * fix get level city when enter game * format code * fix typo, remove some property in the player, add the field cityInfoData to player class
This commit is contained in:
parent
d0dde1c9e2
commit
bdc4b5af89
@ -404,6 +404,10 @@ public final class GameData {
|
||||
private static final Int2ObjectMap<WeaponPromoteData> weaponPromoteDataMap =
|
||||
new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter
|
||||
private static final Int2ObjectMap<StatuePromoteData> statuePromoteDataMap =
|
||||
new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter
|
||||
private static final Int2ObjectMap<WeatherData> weatherDataMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@ -567,6 +571,10 @@ public final class GameData {
|
||||
return weaponPromoteDataMap.get((promoteId << 8) + promoteLevel);
|
||||
}
|
||||
|
||||
public static StatuePromoteData getStatuePromoteData(int cityId, int promoteLevel) {
|
||||
return statuePromoteDataMap.get((cityId << 8) + promoteLevel);
|
||||
}
|
||||
|
||||
public static ReliquaryLevelData getRelicLevelData(int rankLevel, int level) {
|
||||
return reliquaryLevelDataMap.get((rankLevel << 8) + level);
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@ResourceType(name = "StatuePromoteExcelConfigData.json")
|
||||
public class StatuePromoteData extends GameResource {
|
||||
@Getter @Setter private int level;
|
||||
@Getter @Setter private int cityId;
|
||||
@Getter @Setter private ItemParamData[] costItems;
|
||||
@Getter @Setter private int[] rewardIdList;
|
||||
@Getter @Setter private int stamina;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return (cityId << 8) + level;
|
||||
}
|
||||
}
|
28
src/main/java/emu/grasscutter/game/city/CityInfoData.java
Normal file
28
src/main/java/emu/grasscutter/game/city/CityInfoData.java
Normal file
@ -0,0 +1,28 @@
|
||||
package emu.grasscutter.game.city;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.grasscutter.net.proto.CityInfoOuterClass.CityInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Entity
|
||||
public class CityInfoData {
|
||||
@Getter @Setter private int cityId;
|
||||
|
||||
@Getter @Setter
|
||||
private int level = 1; // level of the city (include level SotS, level Frostbearing Trees, etc.)
|
||||
|
||||
@Getter @Setter private int numCrystal = 0; // number of crystals in the city
|
||||
|
||||
public CityInfoData(int cityId) {
|
||||
this.cityId = cityId;
|
||||
}
|
||||
|
||||
public CityInfo toProto() {
|
||||
return CityInfo.newBuilder()
|
||||
.setCityId(cityId)
|
||||
.setLevel(level)
|
||||
.setCrystalNum(numCrystal)
|
||||
.build();
|
||||
}
|
||||
}
|
@ -2,15 +2,24 @@ package emu.grasscutter.game.managers;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.CityData;
|
||||
import emu.grasscutter.data.excels.RewardData;
|
||||
import emu.grasscutter.game.city.CityInfoData;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketLevelupCityRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketSceneForceUnlockNotify;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@ -208,4 +217,91 @@ public class SotSManager extends BasePlayerManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CityData getCityByAreaId(int areaId) {
|
||||
return GameData.getCityDataMap().values().stream()
|
||||
.filter(city -> city.getAreaIdVec().contains(areaId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public CityInfoData getCityInfo(int cityId) {
|
||||
if (player.getCityInfoData() == null) player.setCityInfoData(new HashMap<>());
|
||||
var cityInfo = player.getCityInfoData().get(cityId);
|
||||
if (cityInfo == null) {
|
||||
cityInfo = new CityInfoData(cityId);
|
||||
player.getCityInfoData().put(cityId, cityInfo);
|
||||
}
|
||||
return cityInfo;
|
||||
}
|
||||
|
||||
public void addCityInfo(CityInfoData cityInfoData) {
|
||||
if (player.getCityInfoData() == null) player.setCityInfoData(new HashMap<>());
|
||||
|
||||
player.getCityInfoData().put(cityInfoData.getCityId(), cityInfoData);
|
||||
}
|
||||
|
||||
public void levelUpSotS(int areaId, int sceneId, int itemNum) {
|
||||
if (itemNum <= 0) return;
|
||||
|
||||
// search city by areaId
|
||||
var city = this.getCityByAreaId(areaId);
|
||||
if (city == null) return;
|
||||
var cityId = city.getCityId();
|
||||
|
||||
// check data level up
|
||||
var cityInfo = this.getCityInfo(cityId);
|
||||
var nextStatuePromoteData = GameData.getStatuePromoteData(cityId, cityInfo.getLevel() + 1);
|
||||
if (nextStatuePromoteData == null) return;
|
||||
var nextLevelCrystal = nextStatuePromoteData.getCostItems()[0].getCount();
|
||||
|
||||
// delete item from inventory
|
||||
var itemNumrequired = Math.min(itemNum, nextLevelCrystal - cityInfo.getNumCrystal());
|
||||
player
|
||||
.getInventory()
|
||||
.removeItemById(nextStatuePromoteData.getCostItems()[0].getId(), itemNumrequired);
|
||||
|
||||
// update number oculi
|
||||
cityInfo.setNumCrystal(cityInfo.getNumCrystal() + itemNumrequired);
|
||||
|
||||
// hanble quest
|
||||
if (itemNumrequired >= 1)
|
||||
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_CITY_LEVEL_UP, cityId, areaId);
|
||||
|
||||
// handle oculi overflow
|
||||
if (cityInfo.getNumCrystal() >= nextLevelCrystal) {
|
||||
cityInfo.setNumCrystal(cityInfo.getNumCrystal() - nextLevelCrystal);
|
||||
cityInfo.setLevel(cityInfo.getLevel() + 1);
|
||||
|
||||
// update max stamina and notify client
|
||||
player.setProperty(
|
||||
PlayerProperty.PROP_MAX_STAMINA,
|
||||
player.getProperty(PlayerProperty.PROP_MAX_STAMINA)
|
||||
+ nextStatuePromoteData.getStamina() * 100,
|
||||
true);
|
||||
|
||||
// Add items to inventory
|
||||
if (nextStatuePromoteData.getRewardIdList() != null) {
|
||||
for (var rewardId : nextStatuePromoteData.getRewardIdList()) {
|
||||
RewardData rewardData = GameData.getRewardDataMap().get(rewardId);
|
||||
if (rewardData == null) continue;
|
||||
|
||||
player
|
||||
.getInventory()
|
||||
.addItemParamDatas(rewardData.getRewardItemList(), ActionReason.CityLevelupReward);
|
||||
}
|
||||
}
|
||||
|
||||
// unlock forcescene
|
||||
player.sendPacket(new PacketSceneForceUnlockNotify(1, true));
|
||||
}
|
||||
|
||||
// update data
|
||||
this.addCityInfo(cityInfo);
|
||||
|
||||
// Packets
|
||||
player.sendPacket(
|
||||
new PacketLevelupCityRsp(
|
||||
sceneId, cityInfo.getLevel(), cityId, cityInfo.getNumCrystal(), areaId, 0));
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import emu.grasscutter.game.activity.ActivityManager;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||
import emu.grasscutter.game.battlepass.BattlePassManager;
|
||||
import emu.grasscutter.game.city.CityInfoData;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.expedition.ExpeditionInfo;
|
||||
import emu.grasscutter.game.friends.FriendsList;
|
||||
@ -28,7 +29,6 @@ import emu.grasscutter.game.mail.MailHandler;
|
||||
import emu.grasscutter.game.managers.FurnitureManager;
|
||||
import emu.grasscutter.game.managers.ResinManager;
|
||||
import emu.grasscutter.game.managers.SatiationManager;
|
||||
import emu.grasscutter.game.managers.SotSManager;
|
||||
import emu.grasscutter.game.managers.cooking.ActiveCookCompoundData;
|
||||
import emu.grasscutter.game.managers.cooking.CookingCompoundManager;
|
||||
import emu.grasscutter.game.managers.cooking.CookingManager;
|
||||
@ -38,6 +38,7 @@ import emu.grasscutter.game.managers.forging.ActiveForgeData;
|
||||
import emu.grasscutter.game.managers.forging.ForgingManager;
|
||||
import emu.grasscutter.game.managers.mapmark.MapMark;
|
||||
import emu.grasscutter.game.managers.mapmark.MapMarksManager;
|
||||
import emu.grasscutter.game.managers.SotSManager;
|
||||
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
||||
import emu.grasscutter.game.props.*;
|
||||
import emu.grasscutter.game.quest.QuestManager;
|
||||
@ -221,6 +222,8 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
|
||||
@Getter @Setter private ElementType mainCharacterElement = ElementType.None;
|
||||
|
||||
@Getter @Setter private Map<Integer, CityInfoData> cityInfoData; // cityId -> CityData
|
||||
|
||||
@Deprecated
|
||||
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
|
||||
public Player() {
|
||||
@ -267,6 +270,7 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
this.chatEmojiIdList = new ArrayList<>();
|
||||
this.playerProgress = new PlayerProgress();
|
||||
this.activeQuestTimers = new HashSet<>();
|
||||
this.cityInfoData = new HashMap<>();
|
||||
|
||||
this.attackResults = new LinkedBlockingQueue<>();
|
||||
this.coopRequests = new Int2ObjectOpenHashMap<>();
|
||||
@ -1520,6 +1524,8 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
PropChangeReason.PROP_CHANGE_REASON_PLAYER_ADD_EXP));
|
||||
case PROP_PLAYER_LEVEL -> this.sendPacket(new PacketPlayerPropChangeReasonNotify(this, prop, currentValue, value,
|
||||
PropChangeReason.PROP_CHANGE_REASON_LEVELUP));
|
||||
case PROP_MAX_STAMINA -> this.sendPacket(new PacketPlayerPropChangeReasonNotify(this, prop, currentValue, value,
|
||||
PropChangeReason.PROP_CHANGE_REASON_CITY_LEVELUP));
|
||||
|
||||
// TODO: Handle world level changing.
|
||||
// case PROP_PLAYER_WORLD_LEVEL -> this.sendPacket(new PacketPlayerPropChangeReasonNotify(this, prop, currentValue, value,
|
||||
|
@ -0,0 +1,22 @@
|
||||
package emu.grasscutter.server.packet.recv;
|
||||
|
||||
import emu.grasscutter.net.packet.Opcodes;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.LevelupCityReqOuterClass.LevelupCityReq;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
|
||||
@Opcodes(PacketOpcodes.LevelupCityReq)
|
||||
public class HandlerLevelupCityReq extends PacketHandler {
|
||||
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
LevelupCityReq req = LevelupCityReq.parseFrom(payload);
|
||||
|
||||
// Level up city
|
||||
session
|
||||
.getPlayer()
|
||||
.getSotsManager()
|
||||
.levelUpSotS(req.getAreaId(), req.getSceneId(), req.getItemNum());
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ 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.CityInfoOuterClass.CityInfo;
|
||||
import emu.grasscutter.net.proto.GetSceneAreaRspOuterClass.GetSceneAreaRsp;
|
||||
|
||||
public class PacketGetSceneAreaRsp extends BasePacket {
|
||||
@ -17,9 +16,9 @@ public class PacketGetSceneAreaRsp extends BasePacket {
|
||||
GetSceneAreaRsp.newBuilder()
|
||||
.setSceneId(sceneId)
|
||||
.addAllAreaIdList(player.getUnlockedSceneAreas(sceneId))
|
||||
.addCityInfoList(CityInfo.newBuilder().setCityId(1).setLevel(1).build())
|
||||
.addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build())
|
||||
.addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build())
|
||||
.addCityInfoList(player.getSotsManager().getCityInfo(1).toProto())
|
||||
.addCityInfoList(player.getSotsManager().getCityInfo(2).toProto())
|
||||
.addCityInfoList(player.getSotsManager().getCityInfo(3).toProto())
|
||||
.build();
|
||||
|
||||
this.setData(p);
|
||||
|
@ -0,0 +1,29 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.CityInfoOuterClass.CityInfo;
|
||||
import emu.grasscutter.net.proto.LevelupCityRspOuterClass.LevelupCityRsp;
|
||||
|
||||
public class PacketLevelupCityRsp extends BasePacket {
|
||||
|
||||
public PacketLevelupCityRsp(
|
||||
int sceneId, int level, int cityId, int crystalNum, int areaId, int retcode) {
|
||||
super(PacketOpcodes.LevelupCityRsp);
|
||||
|
||||
LevelupCityRsp proto =
|
||||
LevelupCityRsp.newBuilder()
|
||||
.setSceneId(sceneId)
|
||||
.setCityInfo(
|
||||
CityInfo.newBuilder()
|
||||
.setCityId(cityId)
|
||||
.setLevel(level)
|
||||
.setCrystalNum(crystalNum)
|
||||
.build())
|
||||
.setAreaId(areaId)
|
||||
.setRetcode(retcode)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user