Commit 19d81463 authored by KingRainbow44's avatar KingRainbow44
Browse files

Merge remote-tracking branch 'origin/development' into development

parents 71b118dd e3e917ef
package emu.grasscutter.game.battlepass;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.BattlePassMissionData;
import emu.grasscutter.data.excels.BattlePassRewardData;
import emu.grasscutter.game.props.BattlePassMissionStatus;
import emu.grasscutter.net.proto.BattlePassRewardTagOuterClass.BattlePassRewardTag;
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
@Entity
public class BattlePassReward {
private int level;
private int rewardId;
private boolean paid;
@Transient
private BattlePassMissionData data;
@Deprecated // Morphia only
public BattlePassReward() {}
public BattlePassReward(int level, int rewardId, boolean paid) {
this.level = level;
this.rewardId = rewardId;
this.paid = paid;
}
public int getLevel() {
return level;
}
public int getRewardId() {
return rewardId;
}
public boolean isPaid() {
return paid;
}
public BattlePassRewardTag toProto() {
var protoBuilder = BattlePassRewardTag.newBuilder();
protoBuilder
.setLevel(this.getLevel())
.setRewardId(this.getRewardId())
.setUnlockStatus(this.isPaid() ? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID : BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE);
return protoBuilder.build();
}
}
......@@ -11,6 +11,7 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.scripts.constants.EventType;
......@@ -98,6 +99,8 @@ public class DungeonChallenge extends WorldChallenge {
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
new ScriptArgs(this.isSuccess() ? 1 : 0));
// Battle pass trigger
this.getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON));
}
}
......
......@@ -8,6 +8,7 @@ import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
......@@ -154,6 +155,8 @@ public class EntityMonster extends GameEntity {
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}
}
// Battle Pass trigger
getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
}
public void recalcStats() {
......
......@@ -27,6 +27,7 @@ import emu.grasscutter.game.inventory.Inventory;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.inventory.MaterialType;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.GachaItemOuterClass.GachaItem;
import emu.grasscutter.net.proto.GachaTransferItemOuterClass.GachaTransferItem;
import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp;
......@@ -375,6 +376,9 @@ public class GachaManager {
// Packets
player.sendPacket(new PacketDoGachaRsp(banner, list, gachaInfo));
// Battle Pass trigger
player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_GACHA_NUM, 0, times);
}
private synchronized void startWatcher(GameServer server) {
......
......@@ -33,34 +33,36 @@ import emu.grasscutter.net.proto.SceneReliquaryInfoOuterClass.SceneReliquaryInfo
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
import emu.grasscutter.net.proto.WeaponOuterClass.Weapon;
import emu.grasscutter.utils.WeightedList;
import lombok.Getter;
import lombok.Setter;
@Entity(value = "items", useDiscriminator = false)
public class GameItem {
@Id private ObjectId id;
@Indexed private int ownerId;
private int itemId;
private int count;
@Getter @Setter private int itemId;
@Getter @Setter private int count;
@Transient private long guid; // Player unique id
@Transient private ItemData itemData;
@Transient @Getter private long guid; // Player unique id
@Transient @Getter @Setter private ItemData itemData;
// Equips
private int level;
private int exp;
private int totalExp;
private int promoteLevel;
private boolean locked;
@Getter @Setter private int level;
@Getter @Setter private int exp;
@Getter @Setter private int totalExp;
@Getter @Setter private int promoteLevel;
@Getter @Setter private boolean locked;
// Weapon
private List<Integer> affixes;
private int refinement = 0;
@Getter private List<Integer> affixes;
@Getter @Setter private int refinement = 0;
// Relic
private int mainPropId;
private List<Integer> appendPropIdList;
@Getter @Setter private int mainPropId;
@Getter private List<Integer> appendPropIdList;
private int equipCharacter;
@Transient private int weaponEntityId;
@Getter @Setter private int equipCharacter;
@Transient @Getter @Setter private int weaponEntityId;
public GameItem() {
// Morphia only
......@@ -82,44 +84,39 @@ public class GameItem {
this.itemId = data.getId();
this.itemData = data;
if (data.getItemType() == ItemType.ITEM_VIRTUAL) {
switch (data.getItemType()) {
case ITEM_VIRTUAL:
this.count = count;
} else {
this.count = Math.min(count, data.getStackLimit());
}
// Equip data
if (getItemType() == ItemType.ITEM_WEAPON) {
this.level = Math.max(this.count, 1);
break;
case ITEM_WEAPON:
this.count = 1;
this.level = Math.max(this.count, 1); // ??????????????????
this.affixes = new ArrayList<>(2);
if (getItemData().getSkillAffix() != null) {
for (int skillAffix : getItemData().getSkillAffix()) {
if (data.getSkillAffix() != null) {
for (int skillAffix : data.getSkillAffix()) {
if (skillAffix > 0) {
this.affixes.add(skillAffix);
}
}
}
} else if (getItemType() == ItemType.ITEM_RELIQUARY) {
break;
case ITEM_RELIQUARY:
this.count = 1;
this.level = 1;
this.appendPropIdList = new ArrayList<>();
// Create main property
ReliquaryMainPropData mainPropData = GameDepot.getRandomRelicMainProp(getItemData().getMainPropDepotId());
ReliquaryMainPropData mainPropData = GameDepot.getRandomRelicMainProp(data.getMainPropDepotId());
if (mainPropData != null) {
this.mainPropId = mainPropData.getId();
}
// Create extra stats
if (getItemData().getAppendPropNum() > 0) {
for (int i = 0; i < getItemData().getAppendPropNum(); i++) {
this.addAppendProp();
}
}
this.addAppendProps(data.getAppendPropNum());
break;
default:
this.count = Math.min(count, data.getStackLimit());
}
}
public ObjectId getObjectId() {
return id;
}
public int getOwnerId() {
return ownerId;
}
......@@ -128,162 +125,88 @@ public class GameItem {
this.ownerId = player.getUid();
this.guid = player.getNextGameGuid();
}
public int getItemId() {
return itemId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public long getGuid() {
return guid;
public ObjectId getObjectId() {
return id;
}
public ItemType getItemType() {
return this.itemData.getItemType();
}
public ItemData getItemData() {
return itemData;
}
public void setItemData(ItemData materialData) {
this.itemData = materialData;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int getExp() {
return exp;
}
public void setExp(int exp) {
this.exp = exp;
public static int getMinPromoteLevel(int level) {
if (level > 80) {
return 6;
} else if (level > 70) {
return 5;
} else if (level > 60) {
return 4;
} else if (level > 50) {
return 3;
} else if (level > 40) {
return 2;
} else if (level > 20) {
return 1;
}
public int getTotalExp() {
return totalExp;
}
public void setTotalExp(int totalExp) {
this.totalExp = totalExp;
}
public int getPromoteLevel() {
return promoteLevel;
}
public void setPromoteLevel(int promoteLevel) {
this.promoteLevel = promoteLevel;
return 0;
}
public int getEquipSlot() {
return this.getItemData().getEquipType().getValue();
}
public int getEquipCharacter() {
return equipCharacter;
}
public void setEquipCharacter(int equipCharacter) {
this.equipCharacter = equipCharacter;
}
public boolean isEquipped() {
return this.getEquipCharacter() > 0;
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
public boolean isDestroyable() {
return !this.isLocked() && !this.isEquipped();
}
public int getWeaponEntityId() {
return weaponEntityId;
}
public void setWeaponEntityId(int weaponEntityId) {
this.weaponEntityId = weaponEntityId;
}
public List<Integer> getAffixes() {
return affixes;
}
public int getRefinement() {
return refinement;
public void addAppendProp() {
if (this.appendPropIdList == null) {
this.appendPropIdList = new ArrayList<>();
}
public void setRefinement(int refinement) {
this.refinement = refinement;
if (this.appendPropIdList.size() < 4) {
this.addNewAppendProp();
} else {
this.upgradeRandomAppendProp();
}
public int getMainPropId() {
return mainPropId;
}
public void setMainPropId(int mainPropId) {
this.mainPropId = mainPropId;
public void addAppendProps(int quantity) {
int num = Math.max(quantity, 0);
for (int i = 0; i < num; i++) {
this.addAppendProp();
}
public List<Integer> getAppendPropIdList() {
return appendPropIdList;
}
public void addAppendProp() {
if (this.getAppendPropIdList() == null) {
this.appendPropIdList = new ArrayList<>();
private Set<FightProperty> getAppendFightProperties() {
Set<FightProperty> props = new HashSet<>();
// Previously this would check no more than the first four affixes, however custom artifacts may not respect this order.
for (int appendPropId : this.appendPropIdList) {
ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get(appendPropId);
if (affixData != null) {
props.add(affixData.getFightProp());
}
if (this.getAppendPropIdList().size() < 4) {
addNewAppendProp();
} else {
upgradeRandomAppendProp();
}
return props;
}
private void addNewAppendProp() {
List<ReliquaryAffixData> affixList = GameDepot.getRandomRelicAffixList(getItemData().getAppendPropDepotId());
List<ReliquaryAffixData> affixList = GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
if (affixList == null) {
return;
}
// Build blacklist - Dont add same stat as main/sub stat
Set<FightProperty> blacklist = new HashSet<>();
ReliquaryMainPropData mainPropData = GameData.getReliquaryMainPropDataMap().get(this.getMainPropId());
Set<FightProperty> blacklist = this.getAppendFightProperties();
ReliquaryMainPropData mainPropData = GameData.getReliquaryMainPropDataMap().get(this.mainPropId);
if (mainPropData != null) {
blacklist.add(mainPropData.getFightProp());
}
int len = Math.min(4, this.getAppendPropIdList().size());
for (int i = 0; i < len; i++) {
ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get((int) this.getAppendPropIdList().get(i));
if (affixData != null) {
blacklist.add(affixData.getFightProp());
}
}
// Build random list
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
......@@ -299,25 +222,18 @@ public class GameItem {
// Add random stat
ReliquaryAffixData affixData = randomList.next();
this.getAppendPropIdList().add(affixData.getId());
this.appendPropIdList.add(affixData.getId());
}
private void upgradeRandomAppendProp() {
List<ReliquaryAffixData> affixList = GameDepot.getRandomRelicAffixList(getItemData().getAppendPropDepotId());
List<ReliquaryAffixData> affixList = GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
if (affixList == null) {
return;
}
// Build whitelist
Set<FightProperty> whitelist = new HashSet<>();
int len = Math.min(4, this.getAppendPropIdList().size());
for (int i = 0; i < len; i++) {
ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get((int) this.getAppendPropIdList().get(i));
if (affixData != null) {
whitelist.add(affixData.getFightProp());
}
}
Set<FightProperty> whitelist = this.getAppendFightProperties();
// Build random list
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
......@@ -329,7 +245,7 @@ public class GameItem {
// Add random stat
ReliquaryAffixData affixData = randomList.next();
this.getAppendPropIdList().add(affixData.getId());
this.appendPropIdList.add(affixData.getId());
}
@PostLoad
......
......@@ -18,6 +18,7 @@ import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
......@@ -95,6 +96,7 @@ public class Inventory implements Iterable<GameItem> {
GameItem result = putItem(item);
if (result != null) {
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
return true;
}
......@@ -131,7 +133,9 @@ public class Inventory implements Iterable<GameItem> {
for (GameItem item : items) {
GameItem result = putItem(item);
if (result != null) {
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
changedItems.add(result);
}
}
......@@ -170,22 +174,29 @@ public class Inventory implements Iterable<GameItem> {
InventoryTab tab = getInventoryTab(type);
// Add
if (type == ItemType.ITEM_WEAPON || type == ItemType.ITEM_RELIQUARY) {
switch (type) {
case ITEM_WEAPON:
case ITEM_RELIQUARY:
if (tab.getSize() >= tab.getMaxCapacity()) {
return null;
}
// Duplicates cause problems
item.setCount(Math.max(item.getCount(), 1));
// Adds to inventory
putItem(item, tab);
} else if (type == ItemType.ITEM_VIRTUAL) {
this.putItem(item, tab);
// Set ownership and save to db
item.save();
return item;
case ITEM_VIRTUAL:
// Handle
this.addVirtualItem(item.getItemId(), item.getCount());
return item;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_ADSORBATE) {
default:
switch (item.getItemData().getMaterialType()) {
case MATERIAL_ADSORBATE:
this.player.getEnergyManager().handlePickupElemBall(item);
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) {
case MATERIAL_AVATAR:
// Get avatar id
int avatarId = (item.getItemId() % 1000) + 10000000;
// Dont let people give themselves extra main characters
......@@ -194,56 +205,55 @@ public class Inventory implements Iterable<GameItem> {
}
// Add avatar
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
if (avatarData != null && !player.getAvatars().hasAvatar(avatarId)) {
this.getPlayer().addAvatar(new Avatar(avatarData));
if (avatarData != null && !this.player.getAvatars().hasAvatar(avatarId)) {
this.player.addAvatar(new Avatar(avatarData));
}
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_FLYCLOAK) {
case MATERIAL_FLYCLOAK:
AvatarFlycloakData flycloakData = GameData.getAvatarFlycloakDataMap().get(item.getItemId());
if (flycloakData != null && !player.getFlyCloakList().contains(item.getItemId())) {
getPlayer().addFlycloak(item.getItemId());
if (flycloakData != null && !this.player.getFlyCloakList().contains(item.getItemId())) {
this.player.addFlycloak(item.getItemId());
}
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_COSTUME) {
case MATERIAL_COSTUME:
AvatarCostumeData costumeData = GameData.getAvatarCostumeDataItemIdMap().get(item.getItemId());
if (costumeData != null && !player.getCostumeList().contains(costumeData.getId())) {
getPlayer().addCostume(costumeData.getId());
if (costumeData != null && !this.player.getCostumeList().contains(costumeData.getId())) {
this.player.addCostume(costumeData.getId());
}
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_NAMECARD) {
if (!player.getNameCardList().contains(item.getItemId())) {
getPlayer().addNameCard(item.getItemId());
case MATERIAL_NAMECARD:
if (!this.player.getNameCardList().contains(item.getItemId())) {
this.player.addNameCard(item.getItemId());
}
return null;
} else if (tab != null) {
default:
if (tab == null) {
return null;
}
GameItem existingItem = tab.getItemById(item.getItemId());
if (existingItem == null) {
// Item type didnt exist before, we will add it to main inventory map if there is enough space
if (tab.getSize() >= tab.getMaxCapacity()) {
return null;
}
putItem(item, tab);
this.putItem(item, tab);
// Set ownership and save to db
item.save();
return item;
} else {
// Add count
existingItem.setCount(Math.min(existingItem.getCount() + item.getCount(), item.getItemData().getStackLimit()));
existingItem.save();
return existingItem;
}
} else {
return null;
}
// Set ownership and save to db
if (item.getItemData().getItemType() != ItemType.ITEM_VIRTUAL)
item.save();
return item;
}
}
private synchronized void putItem(GameItem item, InventoryTab tab) {
getPlayer().getCodex().checkAddedItem(item);
this.player.getCodex().checkAddedItem(item);
// Set owner and guid FIRST!
item.setOwner(getPlayer());
item.setOwner(this.player);
// Put in item store
getItems().put(item.getGuid(), item);
if (tab != null) {
......@@ -254,36 +264,36 @@ public class Inventory implements Iterable<GameItem> {
private void addVirtualItem(int itemId, int count) {
switch (itemId) {
case 101 -> // Character exp
getPlayer().getServer().getInventoryManager().upgradeAvatar(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
this.player.getServer().getInventoryManager().upgradeAvatar(this.player, this.player.getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
case 102 -> // Adventure exp
getPlayer().addExpDirectly(count);
this.player.addExpDirectly(count);
case 105 -> // Companionship exp
getPlayer().getServer().getInventoryManager().upgradeAvatarFetterLevel(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
this.player.getServer().getInventoryManager().upgradeAvatarFetterLevel(this.player, this.player.getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
case 106 -> // Resin
getPlayer().getResinManager().addResin(count);
this.player.getResinManager().addResin(count);
case 201 -> // Primogem
getPlayer().setPrimogems(player.getPrimogems() + count);
this.player.setPrimogems(this.player.getPrimogems() + count);
case 202 -> // Mora
getPlayer().setMora(player.getMora() + count);
this.player.setMora(this.player.getMora() + count);
case 203 -> // Genesis Crystals
getPlayer().setCrystals(player.getCrystals() + count);
this.player.setCrystals(this.player.getCrystals() + count);
case 204 -> // Home Coin
getPlayer().setHomeCoin(player.getHomeCoin() + count);
this.player.setHomeCoin(this.player.getHomeCoin() + count);
}
}
private int getVirtualItemCount(int itemId) {
switch (itemId) {
case 201: // Primogem
return player.getPrimogems();
return this.player.getPrimogems();
case 202: // Mora
return player.getMora();
return this.player.getMora();
case 203: // Genesis Crystals
return player.getCrystals();
return this.player.getCrystals();
case 106: // Resin
return player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
return this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
case 204: // Home Coin
return player.getHomeCoin();
return this.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();
......@@ -389,6 +399,10 @@ public class Inventory implements Iterable<GameItem> {
getPlayer().sendPacket(new PacketStoreItemChangeNotify(item));
}
// Battle pass trigger
int removeCount = Math.min(count, item.getCount());
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);
// Update in db
item.save();
......
......@@ -147,7 +147,7 @@ public class InventoryManager {
int totalExp = relic.getTotalExp();
int reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level);
int upgrades = 0;
List<Integer> oldAppendPropIdList = relic.getAppendPropIdList();
List<Integer> oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList());
while (expGain > 0 && reqExp > 0 && level < relic.getItemData().getMaxLevel()) {
// Do calculations
......@@ -169,13 +169,7 @@ public class InventoryManager {
}
}
if (upgrades > 0) {
oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList());
while (upgrades > 0) {
relic.addAppendProp();
upgrades -= 1;
}
}
relic.addAppendProps(upgrades);
// Save
relic.setLevel(level);
......
......@@ -2,6 +2,7 @@ package emu.grasscutter.game.managers;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.server.packet.send.PacketPlayerPropNotify;
import emu.grasscutter.server.packet.send.PacketResinChangeNotify;
import emu.grasscutter.utils.Utils;
......@@ -43,9 +44,11 @@ public class ResinManager {
}
// Send packets.
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
this.player.sendPacket(new PacketResinChangeNotify(this.player));
// Battle Pass trigger
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, 106, amount); // Resin item id = 106
return true;
}
......@@ -66,7 +69,6 @@ public class ResinManager {
}
// Send packets.
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
this.player.sendPacket(new PacketResinChangeNotify(this.player));
}
......@@ -113,7 +115,6 @@ public class ResinManager {
}
// Send packets.
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
this.player.sendPacket(new PacketResinChangeNotify(this.player));
}
......@@ -137,7 +138,6 @@ public class ResinManager {
}
// Send initial notifications on logon.
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
this.player.sendPacket(new PacketResinChangeNotify(this.player));
}
}
......@@ -8,7 +8,6 @@ import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
......@@ -26,7 +25,7 @@ public class SotSManager {
private Timer autoRecoverTimer;
private final boolean enablePriorityHealing = false;
public final static int GlobalMaximumSpringVolume = 8500000;
public final static int GlobalMaximumSpringVolume = PlayerProperty.PROP_MAX_SPRING_VOLUME.getMax();
public SotSManager(Player player) {
this.player = player;
......
......@@ -446,5 +446,10 @@ public class EnergyManager {
public void setEnergyUsage(Boolean energyUsage) {
this.energyUsage = energyUsage;
if (!energyUsage) { // Refill team energy if usage is disabled
for (EntityAvatar entityAvatar : player.getTeamManager().getActiveTeam()) {
entityAvatar.addEnergy(1000, PropChangeReason.PROP_CHANGE_REASON_GM,true);
}
}
}
}
\ No newline at end of file
......@@ -5,18 +5,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.mongodb.QueryBuilder;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.ForgeData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.ForgeStartReqOuterClass;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
import emu.grasscutter.net.proto.ForgeQueueManipulateTypeOuterClass.ForgeQueueManipulateType;
......@@ -148,6 +145,13 @@ public class ForgingManager {
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
//Check if the player has sufficient forge points.
int requiredPoints = forgeData.getForgePoint() * req.getForgeCount();
if (requiredPoints > this.player.getForgePoints()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH));
return;
}
// Check if we have enough of each material and consume.
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
material.add(new ItemParamData(202, forgeData.getScoinCost()));
......@@ -158,6 +162,9 @@ public class ForgingManager {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
}
// Consume forge points.
this.player.setForgePoints(this.player.getForgePoints() - requiredPoints);
// Create and add active forge.
ActiveForgeData activeForge = new ActiveForgeData();
activeForge.setForgeId(req.getForgeId());
......@@ -196,6 +203,9 @@ public class ForgingManager {
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
this.player.getInventory().addItem(addItem);
// Battle pass trigger handler
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_DO_FORGE, 0, finished);
// Replace active forge with a new one for the unfinished items, if there are any.
if (unfinished > 0) {
ActiveForgeData remainingForge = new ActiveForgeData();
......@@ -252,6 +262,12 @@ public class ForgingManager {
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
returnItems.add(returnMora);
// Return forge points to the player.
int requiredPoints = data.getForgePoint() * forge.getCount();
int newPoints = Math.min(this.player.getForgePoints() + requiredPoints, 300_000);
this.player.setForgePoints(newPoints);
// Remove the forge queue.
this.player.getActiveForges().remove(queueId - 1);
this.sendForgeQueueDataNotify(true);
......
......@@ -2,8 +2,6 @@ package emu.grasscutter.game.managers.stamina;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.commands.NoStaminaCommand;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity;
......@@ -112,8 +110,8 @@ public class StaminaManager {
}};
private final Logger logger = Grasscutter.getLogger();
public final static int GlobalCharacterMaximumStamina = 24000;
public final static int GlobalVehicleMaxStamina = 24000;
public final static int GlobalCharacterMaximumStamina = PlayerProperty.PROP_MAX_STAMINA.getMax();
public final static int GlobalVehicleMaxStamina = PlayerProperty.PROP_MAX_STAMINA.getMax();
private Position currentCoordinates = new Position(0, 0, 0);
private Position previousCoordinates = new Position(0, 0, 0);
private MotionState currentState = MotionState.MOTION_STATE_STANDBY;
......@@ -285,14 +283,13 @@ public class StaminaManager {
// Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify
public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) {
// Target Player
if (!GAME_OPTIONS.staminaUsage || session.getPlayer().getStamina()) {
if (!GAME_OPTIONS.staminaUsage || session.getPlayer().getUnlimitedStamina()) {
newStamina = getMaxCharacterStamina();
}
// set stamina if is character stamina
if (isCharacterStamina) {
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
} else {
vehicleStamina = newStamina;
session.send(new PacketVehicleStaminaNotify(vehicleId, ((float) newStamina) / 100));
......
......@@ -7,6 +7,7 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.PlayerLevelData;
import emu.grasscutter.data.excels.WeatherData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.database.DatabaseManager;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.CoopRequest;
import emu.grasscutter.game.ability.AbilityManager;
......@@ -42,6 +43,7 @@ import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.ClimateType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.QuestManager;
import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.tower.TowerData;
......@@ -75,6 +77,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
......@@ -180,6 +185,7 @@ public class Player {
private long springLastUsed;
private HashMap<String, MapMark> mapMarks;
private int nextResinRefresh;
private int lastDailyReset;
@Deprecated
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
......@@ -253,14 +259,14 @@ public class Player {
this.teamManager = new TeamManager(this);
this.birthday = new PlayerBirthday();
this.codex = new PlayerCodex(this);
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1);
this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1);
this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50);
this.setProperty(PlayerProperty.PROP_IS_FLYABLE, 1);
this.setProperty(PlayerProperty.PROP_IS_TRANSFERABLE, 1);
this.setProperty(PlayerProperty.PROP_MAX_STAMINA, 24000);
this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, 24000);
this.setProperty(PlayerProperty.PROP_PLAYER_RESIN, 160);
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1, false);
this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1, false);
this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50, false);
this.setProperty(PlayerProperty.PROP_IS_FLYABLE, 1, false);
this.setProperty(PlayerProperty.PROP_IS_TRANSFERABLE, 1, false);
this.setProperty(PlayerProperty.PROP_MAX_STAMINA, 24000, false);
this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, 24000, false);
this.setProperty(PlayerProperty.PROP_PLAYER_RESIN, 160, false);
this.getFlyCloakList().add(140001);
this.getNameCardList().add(210001);
this.getPos().set(GameConstants.START_POSITION);
......@@ -290,7 +296,9 @@ public class Player {
}
public Account getAccount() {
return account;
if (this.account == null)
this.account = DatabaseHelper.getAccountById(Integer.toString(this.id));
return this.account;
}
public void setAccount(Account account) {
......@@ -429,12 +437,13 @@ public class Player {
return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL);
}
public void setLevel(int level) {
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_LEVEL));
public boolean setLevel(int level) {
if (this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level)) {
this.updateWorldLevel();
this.updateProfile();
return true;
}
return false;
}
public int getExp() {
......@@ -445,47 +454,58 @@ public class Player {
return this.getProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL);
}
public void setWorldLevel(int level) {
this.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_WORLD_LEVEL));
public boolean setWorldLevel(int level) {
if (this.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level)) {
if (this.world.getHost() == this) // Don't update World's WL if we are in someone else's world
this.world.setWorldLevel(level);
this.updateProfile();
return true;
}
return false;
}
public int getForgePoints() {
return this.getProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT);
}
public boolean setForgePoints(int value) {
if (value == this.getForgePoints()) {
return true;
}
return this.setProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT, value);
}
public int getPrimogems() {
return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN);
}
public void setPrimogems(int primogem) {
this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_HCOIN));
public boolean setPrimogems(int primogem) {
return this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem);
}
public int getMora() {
return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN);
}
public void setMora(int mora) {
this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_SCOIN));
public boolean setMora(int mora) {
return this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora);
}
public int getCrystals() {
return this.getProperty(PlayerProperty.PROP_PLAYER_MCOIN);
}
public void setCrystals(int crystals) {
this.setProperty(PlayerProperty.PROP_PLAYER_MCOIN, crystals);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_MCOIN));
public boolean setCrystals(int crystals) {
return this.setProperty(PlayerProperty.PROP_PLAYER_MCOIN, crystals);
}
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));
public boolean setHomeCoin(int coin) {
return this.setProperty(PlayerProperty.PROP_PLAYER_HOME_COIN, coin);
}
private int getExpRequired(int level) {
PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level);
......@@ -523,9 +543,6 @@ public class Player {
// Set exp
this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp);
// Update player with packet
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_EXP));
}
private void updateWorldLevel() {
......@@ -544,7 +561,6 @@ public class Player {
0;
if (newWorldLevel != currentWorldLevel) {
this.getWorld().setWorldLevel(newWorldLevel);
this.setWorldLevel(newWorldLevel);
}
}
......@@ -566,7 +582,7 @@ public class Player {
}
public TowerData getTowerData() {
if(towerData==null){
if (towerData == null) {
// because of mistake, null may be saved as storage at some machine, this if can be removed in future
towerData = new TowerData();
}
......@@ -599,7 +615,11 @@ public class Player {
}
public boolean setProperty(PlayerProperty prop, int value) {
return setPropertyWithSanityCheck(prop, value);
return setPropertyWithSanityCheck(prop, value, true);
}
public boolean setProperty(PlayerProperty prop, int value, boolean sendPacket) {
return setPropertyWithSanityCheck(prop, value, sendPacket);
}
public int getProperty(PlayerProperty prop) {
......@@ -794,6 +814,14 @@ public class Player {
return showAvatarList;
}
public int getLastDailyReset() {
return this.lastDailyReset;
}
public void setLastDailyReset(int value) {
this.lastDailyReset = value;
}
public boolean inMoonCard() {
return moonCard;
}
......@@ -926,12 +954,10 @@ public class Player {
}
this.save();
}
public boolean getStamina() {
// Get Stamina
public boolean getUnlimitedStamina() {
return stamina;
}
public void setStamina(boolean stamina) {
// Set Stamina
public void setUnlimitedStamina(boolean stamina) {
this.stamina = stamina;
}
public boolean inGodmode() {
......@@ -1268,6 +1294,7 @@ public class Player {
public void loadBattlePassManager() {
if (this.battlePassManager != null) return;
this.battlePassManager = DatabaseHelper.loadBattlePass(this);
this.battlePassManager.getMissions().values().removeIf(mission -> mission.getData() == null);
}
public AbilityManager getAbilityManager() {
......@@ -1313,6 +1340,10 @@ public class Player {
this.resetSendPlayerLocTime();
}
}
// Handle daily reset.
this.doDailyReset();
// Expedition
var timeNow = Utils.getCurrentSeconds();
var needNotify = false;
......@@ -1337,8 +1368,24 @@ public class Player {
this.getResinManager().rechargeResin();
}
private void doDailyReset() {
// Check if we should execute a daily reset on this tick.
int currentTime = Utils.getCurrentSeconds();
var currentDate = LocalDate.ofInstant(Instant.ofEpochSecond(currentTime), ZoneId.systemDefault());
var lastResetDate = LocalDate.ofInstant(Instant.ofEpochSecond(this.getLastDailyReset()), ZoneId.systemDefault());
if (!currentDate.isAfter(lastResetDate)) {
return;
}
// We should - now execute all the resetting logic we need.
// Reset forge points.
this.setForgePoints(300_000);
// Done. Update last reset time.
this.setLastDailyReset(currentTime);
}
public void resetSendPlayerLocTime() {
this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000;
......@@ -1405,8 +1452,8 @@ public class Player {
world.addPlayer(this);
// Multiplayer setting
this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber());
this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1);
this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber(), false);
this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1, false);
// Packets
session.send(new PacketPlayerDataNotify(this)); // Player data
......@@ -1425,6 +1472,9 @@ public class Player {
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.
// Battle Pass trigger
this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN);
this.furnitureManager.onLogin();
// Home
home = GameHome.getByUid(getUid());
......@@ -1478,10 +1528,6 @@ public class Player {
// Call quit event.
PlayerQuitEvent event = new PlayerQuitEvent(this); event.call();
//reset wood
getDeforestationManager().resetWood();
}catch (Throwable e){
e.printStackTrace();
Grasscutter.getLogger().warn("Player (UID {}) save failure", getUid());
......@@ -1515,101 +1561,41 @@ public class Player {
this.messageHandler = messageHandler;
}
private void saveSanityCheckedProperty(PlayerProperty prop, int value) {
getProperties().put(prop.getId(), value);
}
private boolean setPropertyWithSanityCheck(PlayerProperty prop, int value) {
if (prop == PlayerProperty.PROP_EXP) { // 1001
if (!(value >= 0)) { return false; }
} else if (prop == PlayerProperty.PROP_BREAK_LEVEL) { // 1002
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_SATIATION_VAL) { // 1003
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_SATIATION_PENALTY_TIME) { // 1004
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_LEVEL) { // 4001
if (!(value >= 0 && value <= 90)) { return false; }
} else if (prop == PlayerProperty.PROP_LAST_CHANGE_AVATAR_TIME) { // 10001
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_MAX_SPRING_VOLUME) { // 10002
if (!(value >= 0 && value <= SotSManager.GlobalMaximumSpringVolume)) { return false; }
} else if (prop == PlayerProperty.PROP_CUR_SPRING_VOLUME) { // 10003
int playerMaximumSpringVolume = getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
if (!(value >= 0 && value <= playerMaximumSpringVolume)) { return false; }
} else if (prop == PlayerProperty.PROP_IS_SPRING_AUTO_USE) { // 10004
if (!(value >= 0 && value <= 1)) { return false; }
} else if (prop == PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT) { // 10005
if (!(value >= 0 && value <= 100)) { return false; }
} else if (prop == PlayerProperty.PROP_IS_FLYABLE) { // 10006
if (!(0 <= value && value <= 1)) { return false; }
} else if (prop == PlayerProperty.PROP_IS_WEATHER_LOCKED) { // 10007
if (!(0 <= value && value <= 1)) { return false; }
} else if (prop == PlayerProperty.PROP_IS_GAME_TIME_LOCKED) { // 10008
if (!(0 <= value && value <= 1)) { return false; }
} else if (prop == PlayerProperty.PROP_IS_TRANSFERABLE) { // 10009
if (!(0 <= value && value <= 1)) { return false; }
} else if (prop == PlayerProperty.PROP_MAX_STAMINA) { // 10010
if (!(value >= 0 && value <= StaminaManager.GlobalCharacterMaximumStamina)) { return false; }
} else if (prop == PlayerProperty.PROP_CUR_PERSIST_STAMINA) { // 10011
int playerMaximumStamina = getProperty(PlayerProperty.PROP_MAX_STAMINA);
if (!(value >= 0 && value <= playerMaximumStamina)) { return false; }
} else if (prop == PlayerProperty.PROP_CUR_TEMPORARY_STAMINA) { // 10012
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_PLAYER_LEVEL) { // 10013
if (!(0 < value && value <= 90)) { return false; }
} else if (prop == PlayerProperty.PROP_PLAYER_EXP) { // 10014
if (!(0 <= value)) { return false; }
} else if (prop == PlayerProperty.PROP_PLAYER_HCOIN) { // 10015
// see PlayerProperty.PROP_PLAYER_HCOIN comments
} else if (prop == PlayerProperty.PROP_PLAYER_SCOIN) { // 10016
// See 10015
} else if (prop == PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE) { // 10017
if (!(0 <= value && value <= 2)) { return false; }
} else if (prop == PlayerProperty.PROP_IS_MP_MODE_AVAILABLE) { // 10018
if (!(0 <= value && value <= 1)) { return false; }
} else if (prop == PlayerProperty.PROP_PLAYER_WORLD_LEVEL) { // 10019
if (!(0 <= value && value <= 8)) { return false; }
} else if (prop == PlayerProperty.PROP_PLAYER_RESIN) { // 10020
// Do not set 160 as a cap, because player can have more than 160 when they use fragile resin.
if (!(0 <= value)) { return false; }
} else if (prop == PlayerProperty.PROP_PLAYER_WAIT_SUB_HCOIN) { // 10022
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_PLAYER_WAIT_SUB_SCOIN) { // 10023
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_IS_ONLY_MP_WITH_PS_PLAYER) { // 10024
if (!(0 <= value && value <= 1)) { return false; }
} else if (prop == PlayerProperty.PROP_PLAYER_MCOIN) { // 10025
// see 10015
} else if (prop == PlayerProperty.PROP_PLAYER_WAIT_SUB_MCOIN) { // 10026
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_PLAYER_LEGENDARY_KEY) { // 10027
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_IS_HAS_FIRST_SHARE) { // 10028
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_PLAYER_FORGE_POINT) { // 10029
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_CUR_CLIMATE_METER) { // 10035
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_CUR_CLIMATE_TYPE) { // 10036
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_CUR_CLIMATE_AREA_ID) { // 10037
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_CUR_CLIMATE_AREA_CLIMATE_TYPE) { // 10038
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_PLAYER_WORLD_LEVEL_LIMIT) { // 10039
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_PLAYER_WORLD_LEVEL_ADJUST_CD) { // 10040
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_PLAYER_LEGENDARY_DAILY_TASK_NUM) { // 10041
// TODO: implement sanity check
} else if (prop == PlayerProperty.PROP_PLAYER_HOME_COIN) { // 10042
if (!(0 <= value)) { return false; }
} else if (prop == PlayerProperty.PROP_PLAYER_WAIT_SUB_HOME_COIN) { // 10043
// TODO: implement sanity check
}
saveSanityCheckedProperty(prop, value);
public int getPropertyMin(PlayerProperty prop) {
if (prop.getDynamicRange()) {
return switch (prop) {
default -> 0;
};
} else {
return prop.getMin();
}
}
public int getPropertyMax(PlayerProperty prop) {
if (prop.getDynamicRange()) {
return switch (prop) {
case PROP_CUR_SPRING_VOLUME -> getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
case PROP_CUR_PERSIST_STAMINA -> getProperty(PlayerProperty.PROP_MAX_STAMINA);
default -> 0;
};
} else {
return prop.getMax();
}
}
private boolean setPropertyWithSanityCheck(PlayerProperty prop, int value, boolean sendPacket) {
int min = this.getPropertyMin(prop);
int max = this.getPropertyMax(prop);
if (min <= value && value <= max) {
this.properties.put(prop.getId(), value);
if (sendPacket) {
// Update player with packet
this.sendPacket(new PacketPlayerPropNotify(this, prop));
}
return true;
} else {
return false;
}
}
}
package emu.grasscutter.game.props;
public enum BattlePassMissionRefreshType {
BATTLE_PASS_MISSION_REFRESH_DAILY (0),
BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE (1), // Weekly
BATTLE_PASS_MISSION_REFRESH_SCHEDULE (2), // Per BP
BATTLE_PASS_MISSION_REFRESH_CYCLE (1); // Event?
private final int value;
BattlePassMissionRefreshType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
package emu.grasscutter.game.props;
import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus;
public enum BattlePassMissionStatus {
MISSION_STATUS_INVALID (0, MissionStatus.MISSION_STATUS_INVALID),
MISSION_STATUS_UNFINISHED (1, MissionStatus.MISSION_STATUS_UNFINISHED),
MISSION_STATUS_FINISHED (2, MissionStatus.MISSION_STATUS_FINISHED),
MISSION_STATUS_POINT_TAKEN (3, MissionStatus.MISSION_STATUS_POINT_TAKEN);
private final int value;
private final MissionStatus missionStatus;
BattlePassMissionStatus(int value, MissionStatus missionStatus) {
this.value = value;
this.missionStatus = missionStatus; // In case proto enum values change later
}
public int getValue() {
return value;
}
public MissionStatus getMissionStatus() {
return missionStatus;
}
}
package emu.grasscutter.game.props;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.util.Map.entry;
import java.util.Arrays;
import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
......@@ -133,4 +139,65 @@ public enum FightProperty {
public static FightProperty getPropByName(String name) {
return stringMap.getOrDefault(name, FIGHT_PROP_NONE);
}
public static FightProperty getPropByShortName(String name) {
return shortNameMap.getOrDefault(name, FIGHT_PROP_NONE);
}
public static Set<String> getShortNames() {
return shortNameMap.keySet();
}
// This was originally for relic properties so some names might not be applicable for e.g. setstats
private static final Map<String, FightProperty> shortNameMap = Map.ofEntries(
// Normal relic stats
entry("hp", FIGHT_PROP_HP),
entry("atk", FIGHT_PROP_ATTACK),
entry("def", FIGHT_PROP_DEFENSE),
entry("hp%", FIGHT_PROP_HP_PERCENT),
entry("atk%", FIGHT_PROP_ATTACK_PERCENT),
entry("def%", FIGHT_PROP_DEFENSE_PERCENT),
entry("em", FIGHT_PROP_ELEMENT_MASTERY),
entry("er", FIGHT_PROP_CHARGE_EFFICIENCY),
entry("hb", FIGHT_PROP_HEAL_ADD),
entry("heal", FIGHT_PROP_HEAL_ADD),
entry("cd", FIGHT_PROP_CRITICAL_HURT),
entry("cdmg", FIGHT_PROP_CRITICAL_HURT),
entry("cr", FIGHT_PROP_CRITICAL),
entry("crate", FIGHT_PROP_CRITICAL),
entry("phys%", FIGHT_PROP_PHYSICAL_ADD_HURT),
entry("dendro%", FIGHT_PROP_GRASS_ADD_HURT),
entry("geo%", FIGHT_PROP_ROCK_ADD_HURT),
entry("anemo%", FIGHT_PROP_WIND_ADD_HURT),
entry("hydro%", FIGHT_PROP_WATER_ADD_HURT),
entry("cryo%", FIGHT_PROP_ICE_ADD_HURT),
entry("electro%", FIGHT_PROP_ELEC_ADD_HURT),
entry("pyro%", FIGHT_PROP_FIRE_ADD_HURT),
// Other stats
entry("maxhp", FIGHT_PROP_MAX_HP),
entry("dmg", FIGHT_PROP_ADD_HURT), // This seems to get reset after attacks
entry("cdr", FIGHT_PROP_SKILL_CD_MINUS_RATIO),
entry("heali", FIGHT_PROP_HEALED_ADD),
entry("shield", FIGHT_PROP_SHIELD_COST_MINUS_RATIO),
entry("defi", FIGHT_PROP_DEFENCE_IGNORE_RATIO),
entry("resall", FIGHT_PROP_SUB_HURT), // This seems to get reset after attacks
entry("resanemo", FIGHT_PROP_WIND_SUB_HURT),
entry("rescryo", FIGHT_PROP_ICE_SUB_HURT),
entry("resdendro", FIGHT_PROP_GRASS_SUB_HURT),
entry("reselectro", FIGHT_PROP_ELEC_SUB_HURT),
entry("resgeo", FIGHT_PROP_ROCK_SUB_HURT),
entry("reshydro", FIGHT_PROP_WATER_SUB_HURT),
entry("respyro", FIGHT_PROP_FIRE_SUB_HURT),
entry("resphys", FIGHT_PROP_PHYSICAL_SUB_HURT)
);
private static final List<FightProperty> flatProps = Arrays.asList(
FIGHT_PROP_BASE_HP, FIGHT_PROP_HP, FIGHT_PROP_BASE_ATTACK, FIGHT_PROP_ATTACK, FIGHT_PROP_BASE_DEFENSE,
FIGHT_PROP_DEFENSE, FIGHT_PROP_HEALED_ADD, FIGHT_PROP_CUR_FIRE_ENERGY, FIGHT_PROP_CUR_ELEC_ENERGY,
FIGHT_PROP_CUR_WATER_ENERGY, FIGHT_PROP_CUR_GRASS_ENERGY, FIGHT_PROP_CUR_WIND_ENERGY, FIGHT_PROP_CUR_ICE_ENERGY,
FIGHT_PROP_CUR_ROCK_ENERGY, FIGHT_PROP_CUR_HP, FIGHT_PROP_MAX_HP, FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_CUR_DEFENSE);
public static boolean isPercentage(FightProperty prop) {
return !flatProps.contains(prop);
}
}
......@@ -6,41 +6,42 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public enum PlayerProperty {
PROP_EXP (1001),
PROP_NONE (0),
PROP_EXP (1001, 0),
PROP_BREAK_LEVEL (1002),
PROP_SATIATION_VAL (1003),
PROP_SATIATION_PENALTY_TIME (1004),
PROP_LEVEL (4001),
PROP_LEVEL (4001, 0, 90),
PROP_LAST_CHANGE_AVATAR_TIME (10001),
PROP_MAX_SPRING_VOLUME (10002), // Maximum volume of the Statue of the Seven for the player [0, 8500000]
PROP_CUR_SPRING_VOLUME (10003), // Current volume of the Statue of the Seven [0, PROP_MAX_SPRING_VOLUME]
PROP_IS_SPRING_AUTO_USE (10004), // Auto HP recovery when approaching the Statue of the Seven [0, 1]
PROP_SPRING_AUTO_USE_PERCENT (10005), // Auto HP recovery percentage [0, 100]
PROP_IS_FLYABLE (10006), // Are you in a state that disables your flying ability? e.g. new player [0, 1]
PROP_IS_WEATHER_LOCKED (10007),
PROP_IS_GAME_TIME_LOCKED (10008),
PROP_IS_TRANSFERABLE (10009),
PROP_MAX_STAMINA (10010), // Maximum stamina of the player (0 - 24000)
PROP_CUR_PERSIST_STAMINA (10011), // Used stamina of the player (0 - PROP_MAX_STAMINA)
PROP_MAX_SPRING_VOLUME (10002, 0, 8_500_000), // Maximum volume of the Statue of the Seven for the player [0, 8500000]
PROP_CUR_SPRING_VOLUME (10003, true), // Current volume of the Statue of the Seven [0, PROP_MAX_SPRING_VOLUME]
PROP_IS_SPRING_AUTO_USE (10004, 0, 1), // Auto HP recovery when approaching the Statue of the Seven [0, 1]
PROP_SPRING_AUTO_USE_PERCENT (10005, 0, 100), // Auto HP recovery percentage [0, 100]
PROP_IS_FLYABLE (10006, 0, 1), // Are you in a state that disables your flying ability? e.g. new player [0, 1]
PROP_IS_WEATHER_LOCKED (10007, 0, 1),
PROP_IS_GAME_TIME_LOCKED (10008, 0, 1),
PROP_IS_TRANSFERABLE (10009, 0, 1),
PROP_MAX_STAMINA (10010, 0, 24_000), // Maximum stamina of the player (0 - 24000)
PROP_CUR_PERSIST_STAMINA (10011, true), // Used stamina of the player (0 - PROP_MAX_STAMINA)
PROP_CUR_TEMPORARY_STAMINA (10012),
PROP_PLAYER_LEVEL (10013),
PROP_PLAYER_LEVEL (10013, 1, 60),
PROP_PLAYER_EXP (10014),
PROP_PLAYER_HCOIN (10015), // Primogem (-inf, +inf)
// It is known that Mihoyo will make Primogem negative in the cases that a player spends
// his gems and then got a money refund, so negative is allowed.
PROP_PLAYER_SCOIN (10016), // Mora [0, +inf)
PROP_PLAYER_MP_SETTING_TYPE (10017), // Do you allow other players to join your game? [0=no 1=direct 2=approval]
PROP_IS_MP_MODE_AVAILABLE (10018), // 0 if in quest or something that disables MP [0, 1]
PROP_PLAYER_WORLD_LEVEL (10019), // [0, 8]
PROP_PLAYER_RESIN (10020), // Original Resin [0, +inf)
PROP_PLAYER_SCOIN (10016, 0), // Mora [0, +inf)
PROP_PLAYER_MP_SETTING_TYPE (10017, 0, 2), // Do you allow other players to join your game? [0=no 1=direct 2=approval]
PROP_IS_MP_MODE_AVAILABLE (10018, 0, 1), // 0 if in quest or something that disables MP [0, 1]
PROP_PLAYER_WORLD_LEVEL (10019, 0, 8), // [0, 8]
PROP_PLAYER_RESIN (10020, 0, 2000), // Original Resin [0, 2000] - note that values above 160 require refills
PROP_PLAYER_WAIT_SUB_HCOIN (10022),
PROP_PLAYER_WAIT_SUB_SCOIN (10023),
PROP_IS_ONLY_MP_WITH_PS_PLAYER (10024), // Is only MP with PlayStation players? [0, 1]
PROP_IS_ONLY_MP_WITH_PS_PLAYER (10024, 0, 1), // Is only MP with PlayStation players? [0, 1]
PROP_PLAYER_MCOIN (10025), // Genesis Crystal (-inf, +inf) see 10015
PROP_PLAYER_WAIT_SUB_MCOIN (10026),
PROP_PLAYER_LEGENDARY_KEY (10027),
PROP_IS_HAS_FIRST_SHARE (10028),
PROP_PLAYER_FORGE_POINT (10029),
PROP_PLAYER_FORGE_POINT (10029, 0, 300_000),
PROP_CUR_CLIMATE_METER (10035),
PROP_CUR_CLIMATE_TYPE (10036),
PROP_CUR_CLIMATE_AREA_ID (10037),
......@@ -48,22 +49,55 @@ public enum PlayerProperty {
PROP_PLAYER_WORLD_LEVEL_LIMIT (10039),
PROP_PLAYER_WORLD_LEVEL_ADJUST_CD (10040),
PROP_PLAYER_LEGENDARY_DAILY_TASK_NUM (10041),
PROP_PLAYER_HOME_COIN (10042), // Realm currency [0, +inf)
PROP_PLAYER_HOME_COIN (10042, 0), // Realm currency [0, +inf)
PROP_PLAYER_WAIT_SUB_HOME_COIN (10043);
private final int id;
private static final int inf = Integer.MAX_VALUE; // Maybe this should be something else?
private final int id, min, max;
private final boolean dynamicRange;
private static final Int2ObjectMap<PlayerProperty> map = new Int2ObjectOpenHashMap<>();
static {
Stream.of(values()).forEach(e -> map.put(e.getId(), e));
}
PlayerProperty(int id) {
PlayerProperty(int id, int min, int max, boolean dynamicRange) {
this.id = id;
this.min = min;
this.max = max;
this.dynamicRange = dynamicRange;
}
PlayerProperty(int id, int min) {
this(id, min, inf, false);
}
PlayerProperty(int id, int min, int max) {
this(id, min, max, false);
}
PlayerProperty(int id) {
this(id, Integer.MIN_VALUE, inf, false);
}
PlayerProperty(int id, boolean dynamicRange) {
this(id, Integer.MIN_VALUE, inf, dynamicRange);
}
public int getId() {
return id;
return this.id;
}
public int getMin() {
return this.min;
}
public int getMax() {
return this.max;
}
public boolean getDynamicRange() {
return dynamicRange;
}
public static PlayerProperty getPropById(int value) {
......
......@@ -9,6 +9,7 @@ import emu.grasscutter.server.game.GameServer;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import static emu.grasscutter.Configuration.*;
......@@ -49,6 +50,12 @@ public class TowerScheduleManager {
return data;
}
public List<Integer> getAllFloors() {
List<Integer> floors = new ArrayList<>(this.getCurrentTowerScheduleData().getEntranceFloorId());
floors.addAll(this.getScheduleFloors());
return floors;
}
public List<Integer> getScheduleFloors() {
return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList();
}
......
......@@ -5,15 +5,19 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.battlepass.BattlePassMissionManager;
import emu.grasscutter.game.combine.CombineManger;
import emu.grasscutter.game.drop.DropManager;
import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.expedition.ExpeditionManager;
import emu.grasscutter.game.gacha.GachaManager;
import emu.grasscutter.game.managers.InventoryManager;
import emu.grasscutter.game.managers.MultiplayerManager;
import emu.grasscutter.game.managers.chat.ChatManager;
import emu.grasscutter.game.managers.chat.ChatManagerHandler;
import emu.grasscutter.game.managers.energy.EnergyManager;
import emu.grasscutter.game.managers.stamina.StaminaManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.ServerQuestHandler;
import emu.grasscutter.game.shop.ShopManager;
......@@ -51,19 +55,19 @@ public final class GameServer extends KcpServer {
private final Set<World> worlds;
private ChatManagerHandler chatManager;
private final InventoryManager inventoryManager;
private final GachaManager gachaManager;
private final ShopManager shopManager;
private final MultiplayerManager multiplayerManager;
private final DungeonManager dungeonManager;
private final ExpeditionManager expeditionManager;
private final CommandMap commandMap;
private final TaskMap taskMap;
private final DropManager dropManager;
private final WorldDataManager worldDataManager;
private final CombineManger combineManger;
private final TowerScheduleManager towerScheduleManager;
@Getter private final InventoryManager inventoryManager;
@Getter private final GachaManager gachaManager;
@Getter private final ShopManager shopManager;
@Getter private final MultiplayerManager multiplayerManager;
@Getter private final DungeonManager dungeonManager;
@Getter private final ExpeditionManager expeditionManager;
@Getter private final CommandMap commandMap;
@Getter private final TaskMap taskMap;
@Getter private final DropManager dropManager;
@Getter private final WorldDataManager worldDataManager;
@Getter private final BattlePassMissionManager battlePassMissionManager;
@Getter private final CombineManger combineManger;
@Getter private final TowerScheduleManager towerScheduleManager;
public GameServer() {
this(getAdapterInetSocketAddress());
......@@ -81,6 +85,10 @@ public final class GameServer extends KcpServer {
this.init(GameSessionManager.getListener(),channelConfig,address);
DungeonChallenge.initialize();
EnergyManager.initialize();
StaminaManager.initialize();
this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.questHandler = new ServerQuestHandler();
......@@ -101,6 +109,8 @@ public final class GameServer extends KcpServer {
this.combineManger = new CombineManger(this);
this.towerScheduleManager = new TowerScheduleManager(this);
this.worldDataManager = new WorldDataManager(this);
this.battlePassMissionManager = new BattlePassMissionManager(this);
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
}
......@@ -129,53 +139,6 @@ public final class GameServer extends KcpServer {
this.chatManager = chatManager;
}
public InventoryManager getInventoryManager() {
return inventoryManager;
}
public GachaManager getGachaManager() {
return gachaManager;
}
public ShopManager getShopManager() {
return shopManager;
}
public MultiplayerManager getMultiplayerManager() {
return multiplayerManager;
}
public DropManager getDropManager() {
return dropManager;
}
public DungeonManager getDungeonManager() {
return dungeonManager;
}
public ExpeditionManager getExpeditionManager() {
return expeditionManager;
}
public CommandMap getCommandMap() {
return this.commandMap;
}
public CombineManger getCombineManger(){
return this.combineManger;
}
public TowerScheduleManager getTowerScheduleManager() {
return towerScheduleManager;
}
public WorldDataManager getWorldDataManager() {
return worldDataManager;
}
public TaskMap getTaskMap() {
return this.taskMap;
}
private static InetSocketAddress getAdapterInetSocketAddress(){
InetSocketAddress inetSocketAddress;
......
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