Skip to content

Commit

Permalink
Levelup City Implementation (#2281)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Phong940253 authored Aug 12, 2023
1 parent d0dde1c commit bdc4b5a
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 5 deletions.
8 changes: 8 additions & 0 deletions src/main/java/emu/grasscutter/data/GameData.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<>();

Expand Down Expand Up @@ -567,6 +571,10 @@ public static WeaponPromoteData getWeaponPromoteData(int promoteId, int promoteL
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);
}
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/emu/grasscutter/data/excels/StatuePromoteData.java
Original file line number Diff line number Diff line change
@@ -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 changes: 28 additions & 0 deletions src/main/java/emu/grasscutter/game/city/CityInfoData.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
96 changes: 96 additions & 0 deletions src/main/java/emu/grasscutter/game/managers/SotSManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@

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;
Expand Down Expand Up @@ -208,4 +217,91 @@ public void run() {
}
}
}

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));
}
}
8 changes: 7 additions & 1 deletion src/main/java/emu/grasscutter/game/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand All @@ -28,7 +29,6 @@
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;
Expand All @@ -38,6 +38,7 @@
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;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -267,6 +270,7 @@ public Player() {
this.chatEmojiIdList = new ArrayList<>();
this.playerProgress = new PlayerProgress();
this.activeQuestTimers = new HashSet<>();
this.cityInfoData = new HashMap<>();

this.attackResults = new LinkedBlockingQueue<>();
this.coopRequests = new Int2ObjectOpenHashMap<>();
Expand Down Expand Up @@ -1520,6 +1524,8 @@ private boolean setPropertyWithSanityCheck(PlayerProperty prop, int value, boole
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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
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 {
Expand All @@ -17,9 +16,9 @@ public PacketGetSceneAreaRsp(Player player, int sceneId) {
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit bdc4b5a

Please sign in to comment.