mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-15 00:15:33 +01: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:
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,
|
||||
|
||||
Reference in New Issue
Block a user