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; ...@@ -11,6 +11,7 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
...@@ -98,6 +99,8 @@ public class DungeonChallenge extends WorldChallenge { ...@@ -98,6 +99,8 @@ public class DungeonChallenge extends WorldChallenge {
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene())); getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
new ScriptArgs(this.isSuccess() ? 1 : 0)); 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; ...@@ -8,6 +8,7 @@ import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
...@@ -154,6 +155,8 @@ public class EntityMonster extends GameEntity { ...@@ -154,6 +155,8 @@ public class EntityMonster extends GameEntity {
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId())); 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() { public void recalcStats() {
......
...@@ -27,6 +27,7 @@ import emu.grasscutter.game.inventory.Inventory; ...@@ -27,6 +27,7 @@ import emu.grasscutter.game.inventory.Inventory;
import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.inventory.MaterialType; import emu.grasscutter.game.inventory.MaterialType;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.GachaItemOuterClass.GachaItem; import emu.grasscutter.net.proto.GachaItemOuterClass.GachaItem;
import emu.grasscutter.net.proto.GachaTransferItemOuterClass.GachaTransferItem; import emu.grasscutter.net.proto.GachaTransferItemOuterClass.GachaTransferItem;
import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp; import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp;
...@@ -372,9 +373,12 @@ public class GachaManager { ...@@ -372,9 +373,12 @@ public class GachaManager {
if (starglitter > 0) { if (starglitter > 0) {
inventory.addItem(starglitterId, starglitter); inventory.addItem(starglitterId, starglitter);
} }
// Packets // Packets
player.sendPacket(new PacketDoGachaRsp(banner, list, gachaInfo)); 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) { private synchronized void startWatcher(GameServer server) {
......
...@@ -33,34 +33,36 @@ import emu.grasscutter.net.proto.SceneReliquaryInfoOuterClass.SceneReliquaryInfo ...@@ -33,34 +33,36 @@ import emu.grasscutter.net.proto.SceneReliquaryInfoOuterClass.SceneReliquaryInfo
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo; import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
import emu.grasscutter.net.proto.WeaponOuterClass.Weapon; import emu.grasscutter.net.proto.WeaponOuterClass.Weapon;
import emu.grasscutter.utils.WeightedList; import emu.grasscutter.utils.WeightedList;
import lombok.Getter;
import lombok.Setter;
@Entity(value = "items", useDiscriminator = false) @Entity(value = "items", useDiscriminator = false)
public class GameItem { public class GameItem {
@Id private ObjectId id; @Id private ObjectId id;
@Indexed private int ownerId; @Indexed private int ownerId;
private int itemId; @Getter @Setter private int itemId;
private int count; @Getter @Setter private int count;
@Transient private long guid; // Player unique id @Transient @Getter private long guid; // Player unique id
@Transient private ItemData itemData; @Transient @Getter @Setter private ItemData itemData;
// Equips // Equips
private int level; @Getter @Setter private int level;
private int exp; @Getter @Setter private int exp;
private int totalExp; @Getter @Setter private int totalExp;
private int promoteLevel; @Getter @Setter private int promoteLevel;
private boolean locked; @Getter @Setter private boolean locked;
// Weapon // Weapon
private List<Integer> affixes; @Getter private List<Integer> affixes;
private int refinement = 0; @Getter @Setter private int refinement = 0;
// Relic // Relic
private int mainPropId; @Getter @Setter private int mainPropId;
private List<Integer> appendPropIdList; @Getter private List<Integer> appendPropIdList;
private int equipCharacter; @Getter @Setter private int equipCharacter;
@Transient private int weaponEntityId; @Transient @Getter @Setter private int weaponEntityId;
public GameItem() { public GameItem() {
// Morphia only // Morphia only
...@@ -82,44 +84,39 @@ public class GameItem { ...@@ -82,44 +84,39 @@ public class GameItem {
this.itemId = data.getId(); this.itemId = data.getId();
this.itemData = data; this.itemData = data;
if (data.getItemType() == ItemType.ITEM_VIRTUAL) { switch (data.getItemType()) {
case ITEM_VIRTUAL:
this.count = count; this.count = count;
} else { break;
this.count = Math.min(count, data.getStackLimit()); case ITEM_WEAPON:
} this.count = 1;
this.level = Math.max(this.count, 1); // ??????????????????
// Equip data this.affixes = new ArrayList<>(2);
if (getItemType() == ItemType.ITEM_WEAPON) { if (data.getSkillAffix() != null) {
this.level = Math.max(this.count, 1); for (int skillAffix : data.getSkillAffix()) {
this.affixes = new ArrayList<>(2); if (skillAffix > 0) {
if (getItemData().getSkillAffix() != null) { this.affixes.add(skillAffix);
for (int skillAffix : getItemData().getSkillAffix()) { }
if (skillAffix > 0) { }
this.affixes.add(skillAffix); }
} break;
} case ITEM_RELIQUARY:
} this.count = 1;
} else if (getItemType() == ItemType.ITEM_RELIQUARY) { this.level = 1;
this.level = 1; this.appendPropIdList = new ArrayList<>();
this.appendPropIdList = new ArrayList<>(); // Create main property
// Create main property ReliquaryMainPropData mainPropData = GameDepot.getRandomRelicMainProp(data.getMainPropDepotId());
ReliquaryMainPropData mainPropData = GameDepot.getRandomRelicMainProp(getItemData().getMainPropDepotId()); if (mainPropData != null) {
if (mainPropData != null) { this.mainPropId = mainPropData.getId();
this.mainPropId = mainPropData.getId(); }
} // Create extra stats
// Create extra stats this.addAppendProps(data.getAppendPropNum());
if (getItemData().getAppendPropNum() > 0) { break;
for (int i = 0; i < getItemData().getAppendPropNum(); i++) { default:
this.addAppendProp(); this.count = Math.min(count, data.getStackLimit());
}
}
} }
} }
public ObjectId getObjectId() {
return id;
}
public int getOwnerId() { public int getOwnerId() {
return ownerId; return ownerId;
} }
...@@ -128,162 +125,88 @@ public class GameItem { ...@@ -128,162 +125,88 @@ public class GameItem {
this.ownerId = player.getUid(); this.ownerId = player.getUid();
this.guid = player.getNextGameGuid(); this.guid = player.getNextGameGuid();
} }
public int getItemId() {
return itemId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public long getGuid() { public ObjectId getObjectId() {
return guid; return id;
} }
public ItemType getItemType() { public ItemType getItemType() {
return this.itemData.getItemType(); return this.itemData.getItemType();
} }
public ItemData getItemData() { public static int getMinPromoteLevel(int level) {
return itemData; if (level > 80) {
} return 6;
} else if (level > 70) {
public void setItemData(ItemData materialData) { return 5;
this.itemData = materialData; } else if (level > 60) {
} return 4;
} else if (level > 50) {
public int getCount() { return 3;
return count; } else if (level > 40) {
} return 2;
} else if (level > 20) {
public void setCount(int count) { return 1;
this.count = count; }
} return 0;
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 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;
} }
public int getEquipSlot() { public int getEquipSlot() {
return this.getItemData().getEquipType().getValue(); return this.getItemData().getEquipType().getValue();
} }
public int getEquipCharacter() {
return equipCharacter;
}
public void setEquipCharacter(int equipCharacter) {
this.equipCharacter = equipCharacter;
}
public boolean isEquipped() { public boolean isEquipped() {
return this.getEquipCharacter() > 0; return this.getEquipCharacter() > 0;
} }
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
public boolean isDestroyable() { public boolean isDestroyable() {
return !this.isLocked() && !this.isEquipped(); 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 setRefinement(int refinement) {
this.refinement = refinement;
}
public int getMainPropId() {
return mainPropId;
}
public void setMainPropId(int mainPropId) {
this.mainPropId = mainPropId;
}
public List<Integer> getAppendPropIdList() {
return appendPropIdList;
}
public void addAppendProp() { public void addAppendProp() {
if (this.getAppendPropIdList() == null) { if (this.appendPropIdList == null) {
this.appendPropIdList = new ArrayList<>(); this.appendPropIdList = new ArrayList<>();
} }
if (this.getAppendPropIdList().size() < 4) { if (this.appendPropIdList.size() < 4) {
addNewAppendProp(); this.addNewAppendProp();
} else { } else {
upgradeRandomAppendProp(); this.upgradeRandomAppendProp();
} }
} }
public void addAppendProps(int quantity) {
int num = Math.max(quantity, 0);
for (int i = 0; i < num; i++) {
this.addAppendProp();
}
}
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());
}
}
return props;
}
private void addNewAppendProp() { private void addNewAppendProp() {
List<ReliquaryAffixData> affixList = GameDepot.getRandomRelicAffixList(getItemData().getAppendPropDepotId()); List<ReliquaryAffixData> affixList = GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
if (affixList == null) { if (affixList == null) {
return; return;
} }
// Build blacklist - Dont add same stat as main/sub stat // Build blacklist - Dont add same stat as main/sub stat
Set<FightProperty> blacklist = new HashSet<>(); Set<FightProperty> blacklist = this.getAppendFightProperties();
ReliquaryMainPropData mainPropData = GameData.getReliquaryMainPropDataMap().get(this.getMainPropId()); ReliquaryMainPropData mainPropData = GameData.getReliquaryMainPropDataMap().get(this.mainPropId);
if (mainPropData != null) { if (mainPropData != null) {
blacklist.add(mainPropData.getFightProp()); 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 // Build random list
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>(); WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
...@@ -299,25 +222,18 @@ public class GameItem { ...@@ -299,25 +222,18 @@ public class GameItem {
// Add random stat // Add random stat
ReliquaryAffixData affixData = randomList.next(); ReliquaryAffixData affixData = randomList.next();
this.getAppendPropIdList().add(affixData.getId()); this.appendPropIdList.add(affixData.getId());
} }
private void upgradeRandomAppendProp() { private void upgradeRandomAppendProp() {
List<ReliquaryAffixData> affixList = GameDepot.getRandomRelicAffixList(getItemData().getAppendPropDepotId()); List<ReliquaryAffixData> affixList = GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
if (affixList == null) { if (affixList == null) {
return; return;
} }
// Build whitelist // Build whitelist
Set<FightProperty> whitelist = new HashSet<>(); Set<FightProperty> whitelist = this.getAppendFightProperties();
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());
}
}
// Build random list // Build random list
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>(); WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
...@@ -329,7 +245,7 @@ public class GameItem { ...@@ -329,7 +245,7 @@ public class GameItem {
// Add random stat // Add random stat
ReliquaryAffixData affixData = randomList.next(); ReliquaryAffixData affixData = randomList.next();
this.getAppendPropIdList().add(affixData.getId()); this.appendPropIdList.add(affixData.getId());
} }
@PostLoad @PostLoad
......
...@@ -18,6 +18,7 @@ import emu.grasscutter.game.avatar.Avatar; ...@@ -18,6 +18,7 @@ import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam; import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
...@@ -95,6 +96,7 @@ public class Inventory implements Iterable<GameItem> { ...@@ -95,6 +96,7 @@ public class Inventory implements Iterable<GameItem> {
GameItem result = putItem(item); GameItem result = putItem(item);
if (result != null) { if (result != null) {
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result)); getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
return true; return true;
} }
...@@ -131,7 +133,9 @@ public class Inventory implements Iterable<GameItem> { ...@@ -131,7 +133,9 @@ public class Inventory implements Iterable<GameItem> {
for (GameItem item : items) { for (GameItem item : items) {
GameItem result = putItem(item); GameItem result = putItem(item);
if (result != null) { if (result != null) {
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
changedItems.add(result); changedItems.add(result);
} }
} }
...@@ -170,80 +174,86 @@ public class Inventory implements Iterable<GameItem> { ...@@ -170,80 +174,86 @@ public class Inventory implements Iterable<GameItem> {
InventoryTab tab = getInventoryTab(type); InventoryTab tab = getInventoryTab(type);
// Add // Add
if (type == ItemType.ITEM_WEAPON || type == ItemType.ITEM_RELIQUARY) { switch (type) {
if (tab.getSize() >= tab.getMaxCapacity()) { case ITEM_WEAPON:
return null; case ITEM_RELIQUARY:
}
// Duplicates cause problems
item.setCount(Math.max(item.getCount(), 1));
// Adds to inventory
putItem(item, tab);
} else if (type == ItemType.ITEM_VIRTUAL) {
// Handle
this.addVirtualItem(item.getItemId(), item.getCount());
return item;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_ADSORBATE) {
this.player.getEnergyManager().handlePickupElemBall(item);
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) {
// Get avatar id
int avatarId = (item.getItemId() % 1000) + 10000000;
// Dont let people give themselves extra main characters
if (avatarId == GameConstants.MAIN_CHARACTER_MALE || avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
return null;
}
// Add avatar
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
if (avatarData != null && !player.getAvatars().hasAvatar(avatarId)) {
this.getPlayer().addAvatar(new Avatar(avatarData));
}
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_FLYCLOAK) {
AvatarFlycloakData flycloakData = GameData.getAvatarFlycloakDataMap().get(item.getItemId());
if (flycloakData != null && !player.getFlyCloakList().contains(item.getItemId())) {
getPlayer().addFlycloak(item.getItemId());
}
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_COSTUME) {
AvatarCostumeData costumeData = GameData.getAvatarCostumeDataItemIdMap().get(item.getItemId());
if (costumeData != null && !player.getCostumeList().contains(costumeData.getId())) {
getPlayer().addCostume(costumeData.getId());
}
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_NAMECARD) {
if (!player.getNameCardList().contains(item.getItemId())) {
getPlayer().addNameCard(item.getItemId());
}
return null;
} else if (tab != 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()) { if (tab.getSize() >= tab.getMaxCapacity()) {
return null; return null;
} }
putItem(item, tab); // Duplicates cause problems
} else { item.setCount(Math.max(item.getCount(), 1));
// Add count // Adds to inventory
existingItem.setCount(Math.min(existingItem.getCount() + item.getCount(), item.getItemData().getStackLimit())); this.putItem(item, tab);
existingItem.save(); // Set ownership and save to db
return existingItem; item.save();
} return item;
} else { case ITEM_VIRTUAL:
return null; // Handle
} this.addVirtualItem(item.getItemId(), item.getCount());
return item;
// Set ownership and save to db default:
if (item.getItemData().getItemType() != ItemType.ITEM_VIRTUAL) switch (item.getItemData().getMaterialType()) {
item.save(); case MATERIAL_ADSORBATE:
this.player.getEnergyManager().handlePickupElemBall(item);
return item; return null;
case MATERIAL_AVATAR:
// Get avatar id
int avatarId = (item.getItemId() % 1000) + 10000000;
// Dont let people give themselves extra main characters
if (avatarId == GameConstants.MAIN_CHARACTER_MALE || avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
return null;
}
// Add avatar
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
if (avatarData != null && !this.player.getAvatars().hasAvatar(avatarId)) {
this.player.addAvatar(new Avatar(avatarData));
}
return null;
case MATERIAL_FLYCLOAK:
AvatarFlycloakData flycloakData = GameData.getAvatarFlycloakDataMap().get(item.getItemId());
if (flycloakData != null && !this.player.getFlyCloakList().contains(item.getItemId())) {
this.player.addFlycloak(item.getItemId());
}
return null;
case MATERIAL_COSTUME:
AvatarCostumeData costumeData = GameData.getAvatarCostumeDataItemIdMap().get(item.getItemId());
if (costumeData != null && !this.player.getCostumeList().contains(costumeData.getId())) {
this.player.addCostume(costumeData.getId());
}
return null;
case MATERIAL_NAMECARD:
if (!this.player.getNameCardList().contains(item.getItemId())) {
this.player.addNameCard(item.getItemId());
}
return 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;
}
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;
}
}
}
} }
private synchronized void putItem(GameItem item, InventoryTab tab) { private synchronized void putItem(GameItem item, InventoryTab tab) {
getPlayer().getCodex().checkAddedItem(item); this.player.getCodex().checkAddedItem(item);
// Set owner and guid FIRST! // Set owner and guid FIRST!
item.setOwner(getPlayer()); item.setOwner(this.player);
// Put in item store // Put in item store
getItems().put(item.getGuid(), item); getItems().put(item.getGuid(), item);
if (tab != null) { if (tab != null) {
...@@ -254,36 +264,36 @@ public class Inventory implements Iterable<GameItem> { ...@@ -254,36 +264,36 @@ public class Inventory implements Iterable<GameItem> {
private void addVirtualItem(int itemId, int count) { private void addVirtualItem(int itemId, int count) {
switch (itemId) { switch (itemId) {
case 101 -> // Character exp 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 case 102 -> // Adventure exp
getPlayer().addExpDirectly(count); this.player.addExpDirectly(count);
case 105 -> // Companionship exp 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 case 106 -> // Resin
getPlayer().getResinManager().addResin(count); this.player.getResinManager().addResin(count);
case 201 -> // Primogem case 201 -> // Primogem
getPlayer().setPrimogems(player.getPrimogems() + count); this.player.setPrimogems(this.player.getPrimogems() + count);
case 202 -> // Mora case 202 -> // Mora
getPlayer().setMora(player.getMora() + count); this.player.setMora(this.player.getMora() + count);
case 203 -> // Genesis Crystals case 203 -> // Genesis Crystals
getPlayer().setCrystals(player.getCrystals() + count); this.player.setCrystals(this.player.getCrystals() + count);
case 204 -> // Home Coin case 204 -> // Home Coin
getPlayer().setHomeCoin(player.getHomeCoin() + count); this.player.setHomeCoin(this.player.getHomeCoin() + count);
} }
} }
private int getVirtualItemCount(int itemId) { private int getVirtualItemCount(int itemId) {
switch (itemId) { switch (itemId) {
case 201: // Primogem case 201: // Primogem
return player.getPrimogems(); return this.player.getPrimogems();
case 202: // Mora case 202: // Mora
return player.getMora(); return this.player.getMora();
case 203: // Genesis Crystals case 203: // Genesis Crystals
return player.getCrystals(); return this.player.getCrystals();
case 106: // Resin case 106: // Resin
return player.getProperty(PlayerProperty.PROP_PLAYER_RESIN); return this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
case 204: // Home Coin case 204: // Home Coin
return player.getHomeCoin(); return this.player.getHomeCoin();
default: default:
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S 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(); return (item == null) ? 0 : item.getCount();
...@@ -368,7 +378,7 @@ public class Inventory implements Iterable<GameItem> { ...@@ -368,7 +378,7 @@ public class Inventory implements Iterable<GameItem> {
if (count <= 0 || item == null) { if (count <= 0 || item == null) {
return false; return false;
} }
if (item.getItemData().isEquip()) { if (item.getItemData().isEquip()) {
item.setCount(0); item.setCount(0);
} else { } else {
...@@ -389,6 +399,10 @@ public class Inventory implements Iterable<GameItem> { ...@@ -389,6 +399,10 @@ public class Inventory implements Iterable<GameItem> {
getPlayer().sendPacket(new PacketStoreItemChangeNotify(item)); 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 // Update in db
item.save(); item.save();
......
...@@ -147,7 +147,7 @@ public class InventoryManager { ...@@ -147,7 +147,7 @@ public class InventoryManager {
int totalExp = relic.getTotalExp(); int totalExp = relic.getTotalExp();
int reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level); int reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level);
int upgrades = 0; int upgrades = 0;
List<Integer> oldAppendPropIdList = relic.getAppendPropIdList(); List<Integer> oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList());
while (expGain > 0 && reqExp > 0 && level < relic.getItemData().getMaxLevel()) { while (expGain > 0 && reqExp > 0 && level < relic.getItemData().getMaxLevel()) {
// Do calculations // Do calculations
...@@ -169,13 +169,7 @@ public class InventoryManager { ...@@ -169,13 +169,7 @@ public class InventoryManager {
} }
} }
if (upgrades > 0) { relic.addAppendProps(upgrades);
oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList());
while (upgrades > 0) {
relic.addAppendProp();
upgrades -= 1;
}
}
// Save // Save
relic.setLevel(level); relic.setLevel(level);
......
...@@ -2,6 +2,7 @@ package emu.grasscutter.game.managers; ...@@ -2,6 +2,7 @@ package emu.grasscutter.game.managers;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty; 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.PacketPlayerPropNotify;
import emu.grasscutter.server.packet.send.PacketResinChangeNotify; import emu.grasscutter.server.packet.send.PacketResinChangeNotify;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
...@@ -43,9 +44,11 @@ public class ResinManager { ...@@ -43,9 +44,11 @@ public class ResinManager {
} }
// Send packets. // Send packets.
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
this.player.sendPacket(new PacketResinChangeNotify(this.player)); 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; return true;
} }
...@@ -66,7 +69,6 @@ public class ResinManager { ...@@ -66,7 +69,6 @@ public class ResinManager {
} }
// Send packets. // Send packets.
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
this.player.sendPacket(new PacketResinChangeNotify(this.player)); this.player.sendPacket(new PacketResinChangeNotify(this.player));
} }
...@@ -113,7 +115,6 @@ public class ResinManager { ...@@ -113,7 +115,6 @@ public class ResinManager {
} }
// Send packets. // Send packets.
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
this.player.sendPacket(new PacketResinChangeNotify(this.player)); this.player.sendPacket(new PacketResinChangeNotify(this.player));
} }
...@@ -137,7 +138,6 @@ public class ResinManager { ...@@ -137,7 +138,6 @@ public class ResinManager {
} }
// Send initial notifications on logon. // Send initial notifications on logon.
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
this.player.sendPacket(new PacketResinChangeNotify(this.player)); this.player.sendPacket(new PacketResinChangeNotify(this.player));
} }
} }
...@@ -8,7 +8,6 @@ import emu.grasscutter.game.props.FightProperty; ...@@ -8,7 +8,6 @@ import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason; import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; 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.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
...@@ -26,7 +25,7 @@ public class SotSManager { ...@@ -26,7 +25,7 @@ public class SotSManager {
private Timer autoRecoverTimer; private Timer autoRecoverTimer;
private final boolean enablePriorityHealing = false; 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) { public SotSManager(Player player) {
this.player = player; this.player = player;
......
...@@ -446,5 +446,10 @@ public class EnergyManager { ...@@ -446,5 +446,10 @@ public class EnergyManager {
public void setEnergyUsage(Boolean energyUsage) { public void setEnergyUsage(Boolean energyUsage) {
this.energyUsage = 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; ...@@ -5,18 +5,15 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.mongodb.QueryBuilder;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.ForgeData; import emu.grasscutter.data.excels.ForgeData;
import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; 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.ForgeQueueDataOuterClass.ForgeQueueData;
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq; import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
import emu.grasscutter.net.proto.ForgeQueueManipulateTypeOuterClass.ForgeQueueManipulateType; import emu.grasscutter.net.proto.ForgeQueueManipulateTypeOuterClass.ForgeQueueManipulateType;
...@@ -147,6 +144,13 @@ public class ForgingManager { ...@@ -147,6 +144,13 @@ public class ForgingManager {
} }
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId()); 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. // Check if we have enough of each material and consume.
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems()); List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
...@@ -158,6 +162,9 @@ public class ForgingManager { ...@@ -158,6 +162,9 @@ public class ForgingManager {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code. 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. // Create and add active forge.
ActiveForgeData activeForge = new ActiveForgeData(); ActiveForgeData activeForge = new ActiveForgeData();
activeForge.setForgeId(req.getForgeId()); activeForge.setForgeId(req.getForgeId());
...@@ -195,6 +202,9 @@ public class ForgingManager { ...@@ -195,6 +202,9 @@ public class ForgingManager {
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished); GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
this.player.getInventory().addItem(addItem); 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. // Replace active forge with a new one for the unfinished items, if there are any.
if (unfinished > 0) { if (unfinished > 0) {
...@@ -252,6 +262,12 @@ public class ForgingManager { ...@@ -252,6 +262,12 @@ public class ForgingManager {
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount()); GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
returnItems.add(returnMora); 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. // Remove the forge queue.
this.player.getActiveForges().remove(queueId - 1); this.player.getActiveForges().remove(queueId - 1);
this.sendForgeQueueDataNotify(true); this.sendForgeQueueDataNotify(true);
......
...@@ -2,8 +2,6 @@ package emu.grasscutter.game.managers.stamina; ...@@ -2,8 +2,6 @@ package emu.grasscutter.game.managers.stamina;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import emu.grasscutter.Grasscutter; 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.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
...@@ -112,8 +110,8 @@ public class StaminaManager { ...@@ -112,8 +110,8 @@ public class StaminaManager {
}}; }};
private final Logger logger = Grasscutter.getLogger(); private final Logger logger = Grasscutter.getLogger();
public final static int GlobalCharacterMaximumStamina = 24000; public final static int GlobalCharacterMaximumStamina = PlayerProperty.PROP_MAX_STAMINA.getMax();
public final static int GlobalVehicleMaxStamina = 24000; public final static int GlobalVehicleMaxStamina = PlayerProperty.PROP_MAX_STAMINA.getMax();
private Position currentCoordinates = new Position(0, 0, 0); private Position currentCoordinates = new Position(0, 0, 0);
private Position previousCoordinates = new Position(0, 0, 0); private Position previousCoordinates = new Position(0, 0, 0);
private MotionState currentState = MotionState.MOTION_STATE_STANDBY; private MotionState currentState = MotionState.MOTION_STATE_STANDBY;
...@@ -285,14 +283,13 @@ public class StaminaManager { ...@@ -285,14 +283,13 @@ public class StaminaManager {
// Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify // Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify
public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) { public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) {
// Target Player // Target Player
if (!GAME_OPTIONS.staminaUsage || session.getPlayer().getStamina()) { if (!GAME_OPTIONS.staminaUsage || session.getPlayer().getUnlimitedStamina()) {
newStamina = getMaxCharacterStamina(); newStamina = getMaxCharacterStamina();
} }
// set stamina if is character stamina // set stamina if is character stamina
if (isCharacterStamina) { if (isCharacterStamina) {
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina); player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
} else { } else {
vehicleStamina = newStamina; vehicleStamina = newStamina;
session.send(new PacketVehicleStaminaNotify(vehicleId, ((float) newStamina) / 100)); session.send(new PacketVehicleStaminaNotify(vehicleId, ((float) newStamina) / 100));
......
...@@ -7,6 +7,7 @@ import emu.grasscutter.data.GameData; ...@@ -7,6 +7,7 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.PlayerLevelData; import emu.grasscutter.data.excels.PlayerLevelData;
import emu.grasscutter.data.excels.WeatherData; import emu.grasscutter.data.excels.WeatherData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.database.DatabaseManager;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.CoopRequest; import emu.grasscutter.game.CoopRequest;
import emu.grasscutter.game.ability.AbilityManager; import emu.grasscutter.game.ability.AbilityManager;
...@@ -42,6 +43,7 @@ import emu.grasscutter.game.props.ActionReason; ...@@ -42,6 +43,7 @@ import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.ClimateType; import emu.grasscutter.game.props.ClimateType;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.SceneType; import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.QuestManager; import emu.grasscutter.game.quest.QuestManager;
import emu.grasscutter.game.shop.ShopLimit; import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.tower.TowerData; import emu.grasscutter.game.tower.TowerData;
...@@ -75,6 +77,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ...@@ -75,6 +77,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*; import java.util.*;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
...@@ -180,6 +185,7 @@ public class Player { ...@@ -180,6 +185,7 @@ public class Player {
private long springLastUsed; private long springLastUsed;
private HashMap<String, MapMark> mapMarks; private HashMap<String, MapMark> mapMarks;
private int nextResinRefresh; private int nextResinRefresh;
private int lastDailyReset;
@Deprecated @Deprecated
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only! @SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
...@@ -253,14 +259,14 @@ public class Player { ...@@ -253,14 +259,14 @@ public class Player {
this.teamManager = new TeamManager(this); this.teamManager = new TeamManager(this);
this.birthday = new PlayerBirthday(); this.birthday = new PlayerBirthday();
this.codex = new PlayerCodex(this); this.codex = new PlayerCodex(this);
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1); this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1, false);
this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1); this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1, false);
this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50); this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50, false);
this.setProperty(PlayerProperty.PROP_IS_FLYABLE, 1); this.setProperty(PlayerProperty.PROP_IS_FLYABLE, 1, false);
this.setProperty(PlayerProperty.PROP_IS_TRANSFERABLE, 1); this.setProperty(PlayerProperty.PROP_IS_TRANSFERABLE, 1, false);
this.setProperty(PlayerProperty.PROP_MAX_STAMINA, 24000); this.setProperty(PlayerProperty.PROP_MAX_STAMINA, 24000, false);
this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, 24000); this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, 24000, false);
this.setProperty(PlayerProperty.PROP_PLAYER_RESIN, 160); this.setProperty(PlayerProperty.PROP_PLAYER_RESIN, 160, false);
this.getFlyCloakList().add(140001); this.getFlyCloakList().add(140001);
this.getNameCardList().add(210001); this.getNameCardList().add(210001);
this.getPos().set(GameConstants.START_POSITION); this.getPos().set(GameConstants.START_POSITION);
...@@ -290,7 +296,9 @@ public class Player { ...@@ -290,7 +296,9 @@ public class Player {
} }
public Account getAccount() { 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) { public void setAccount(Account account) {
...@@ -429,12 +437,13 @@ public class Player { ...@@ -429,12 +437,13 @@ public class Player {
return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL); return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL);
} }
public void setLevel(int level) { public boolean setLevel(int level) {
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level); if (this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level)) {
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_LEVEL)); this.updateWorldLevel();
this.updateProfile();
this.updateWorldLevel(); return true;
this.updateProfile(); }
return false;
} }
public int getExp() { public int getExp() {
...@@ -444,48 +453,59 @@ public class Player { ...@@ -444,48 +453,59 @@ public class Player {
public int getWorldLevel() { public int getWorldLevel() {
return this.getProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL); return this.getProperty(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 void setWorldLevel(int level) { public int getForgePoints() {
this.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level); return this.getProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_WORLD_LEVEL)); }
this.updateProfile(); public boolean setForgePoints(int value) {
if (value == this.getForgePoints()) {
return true;
}
return this.setProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT, value);
} }
public int getPrimogems() { public int getPrimogems() {
return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN); return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN);
} }
public void setPrimogems(int primogem) { public boolean setPrimogems(int primogem) {
this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem); return this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_HCOIN));
} }
public int getMora() { public int getMora() {
return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN); return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN);
} }
public void setMora(int mora) { public boolean setMora(int mora) {
this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora); return this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_SCOIN));
} }
public int getCrystals() { public int getCrystals() {
return this.getProperty(PlayerProperty.PROP_PLAYER_MCOIN); return this.getProperty(PlayerProperty.PROP_PLAYER_MCOIN);
} }
public void setCrystals(int crystals) { public boolean setCrystals(int crystals) {
this.setProperty(PlayerProperty.PROP_PLAYER_MCOIN, crystals); return this.setProperty(PlayerProperty.PROP_PLAYER_MCOIN, crystals);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_MCOIN));
} }
public int getHomeCoin() { public int getHomeCoin() {
return this.getProperty(PlayerProperty.PROP_PLAYER_HOME_COIN); return this.getProperty(PlayerProperty.PROP_PLAYER_HOME_COIN);
} }
public void setHomeCoin(int coin) { public boolean setHomeCoin(int coin) {
this.setProperty(PlayerProperty.PROP_PLAYER_HOME_COIN, coin); return this.setProperty(PlayerProperty.PROP_PLAYER_HOME_COIN, coin);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_HOME_COIN));
} }
private int getExpRequired(int level) { private int getExpRequired(int level) {
PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level); PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level);
...@@ -523,9 +543,6 @@ public class Player { ...@@ -523,9 +543,6 @@ public class Player {
// Set exp // Set exp
this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp); this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp);
// Update player with packet
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_EXP));
} }
private void updateWorldLevel() { private void updateWorldLevel() {
...@@ -544,7 +561,6 @@ public class Player { ...@@ -544,7 +561,6 @@ public class Player {
0; 0;
if (newWorldLevel != currentWorldLevel) { if (newWorldLevel != currentWorldLevel) {
this.getWorld().setWorldLevel(newWorldLevel);
this.setWorldLevel(newWorldLevel); this.setWorldLevel(newWorldLevel);
} }
} }
...@@ -566,7 +582,7 @@ public class Player { ...@@ -566,7 +582,7 @@ public class Player {
} }
public TowerData getTowerData() { 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 // because of mistake, null may be saved as storage at some machine, this if can be removed in future
towerData = new TowerData(); towerData = new TowerData();
} }
...@@ -599,7 +615,11 @@ public class Player { ...@@ -599,7 +615,11 @@ public class Player {
} }
public boolean setProperty(PlayerProperty prop, int value) { 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) { public int getProperty(PlayerProperty prop) {
...@@ -794,6 +814,14 @@ public class Player { ...@@ -794,6 +814,14 @@ public class Player {
return showAvatarList; return showAvatarList;
} }
public int getLastDailyReset() {
return this.lastDailyReset;
}
public void setLastDailyReset(int value) {
this.lastDailyReset = value;
}
public boolean inMoonCard() { public boolean inMoonCard() {
return moonCard; return moonCard;
} }
...@@ -926,12 +954,10 @@ public class Player { ...@@ -926,12 +954,10 @@ public class Player {
} }
this.save(); this.save();
} }
public boolean getStamina() { public boolean getUnlimitedStamina() {
// Get Stamina
return stamina; return stamina;
} }
public void setStamina(boolean stamina) { public void setUnlimitedStamina(boolean stamina) {
// Set Stamina
this.stamina = stamina; this.stamina = stamina;
} }
public boolean inGodmode() { public boolean inGodmode() {
...@@ -1268,6 +1294,7 @@ public class Player { ...@@ -1268,6 +1294,7 @@ public class Player {
public void loadBattlePassManager() { public void loadBattlePassManager() {
if (this.battlePassManager != null) return; if (this.battlePassManager != null) return;
this.battlePassManager = DatabaseHelper.loadBattlePass(this); this.battlePassManager = DatabaseHelper.loadBattlePass(this);
this.battlePassManager.getMissions().values().removeIf(mission -> mission.getData() == null);
} }
public AbilityManager getAbilityManager() { public AbilityManager getAbilityManager() {
...@@ -1313,6 +1340,10 @@ public class Player { ...@@ -1313,6 +1340,10 @@ public class Player {
this.resetSendPlayerLocTime(); this.resetSendPlayerLocTime();
} }
} }
// Handle daily reset.
this.doDailyReset();
// Expedition // Expedition
var timeNow = Utils.getCurrentSeconds(); var timeNow = Utils.getCurrentSeconds();
var needNotify = false; var needNotify = false;
...@@ -1337,8 +1368,24 @@ public class Player { ...@@ -1337,8 +1368,24 @@ public class Player {
this.getResinManager().rechargeResin(); 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() { public void resetSendPlayerLocTime() {
this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000; this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000;
...@@ -1405,8 +1452,8 @@ public class Player { ...@@ -1405,8 +1452,8 @@ public class Player {
world.addPlayer(this); world.addPlayer(this);
// Multiplayer setting // Multiplayer setting
this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber()); this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber(), false);
this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1); this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1, false);
// Packets // Packets
session.send(new PacketPlayerDataNotify(this)); // Player data session.send(new PacketPlayerDataNotify(this)); // Player data
...@@ -1425,6 +1472,9 @@ public class Player { ...@@ -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. 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(); this.furnitureManager.onLogin();
// Home // Home
home = GameHome.getByUid(getUid()); home = GameHome.getByUid(getUid());
...@@ -1478,10 +1528,6 @@ public class Player { ...@@ -1478,10 +1528,6 @@ public class Player {
// Call quit event. // Call quit event.
PlayerQuitEvent event = new PlayerQuitEvent(this); event.call(); PlayerQuitEvent event = new PlayerQuitEvent(this); event.call();
//reset wood
getDeforestationManager().resetWood();
}catch (Throwable e){ }catch (Throwable e){
e.printStackTrace(); e.printStackTrace();
Grasscutter.getLogger().warn("Player (UID {}) save failure", getUid()); Grasscutter.getLogger().warn("Player (UID {}) save failure", getUid());
...@@ -1515,101 +1561,41 @@ public class Player { ...@@ -1515,101 +1561,41 @@ public class Player {
this.messageHandler = messageHandler; this.messageHandler = messageHandler;
} }
private void saveSanityCheckedProperty(PlayerProperty prop, int value) { public int getPropertyMin(PlayerProperty prop) {
getProperties().put(prop.getId(), value); if (prop.getDynamicRange()) {
} return switch (prop) {
default -> 0;
private boolean setPropertyWithSanityCheck(PlayerProperty prop, int value) { };
if (prop == PlayerProperty.PROP_EXP) { // 1001 } else {
if (!(value >= 0)) { return false; } return prop.getMin();
} else if (prop == PlayerProperty.PROP_BREAK_LEVEL) { // 1002 }
// TODO: implement sanity check }
} else if (prop == PlayerProperty.PROP_SATIATION_VAL) { // 1003
// TODO: implement sanity check public int getPropertyMax(PlayerProperty prop) {
} else if (prop == PlayerProperty.PROP_SATIATION_PENALTY_TIME) { // 1004 if (prop.getDynamicRange()) {
// TODO: implement sanity check return switch (prop) {
} else if (prop == PlayerProperty.PROP_LEVEL) { // 4001 case PROP_CUR_SPRING_VOLUME -> getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
if (!(value >= 0 && value <= 90)) { return false; } case PROP_CUR_PERSIST_STAMINA -> getProperty(PlayerProperty.PROP_MAX_STAMINA);
} else if (prop == PlayerProperty.PROP_LAST_CHANGE_AVATAR_TIME) { // 10001 default -> 0;
// TODO: implement sanity check };
} else if (prop == PlayerProperty.PROP_MAX_SPRING_VOLUME) { // 10002 } else {
if (!(value >= 0 && value <= SotSManager.GlobalMaximumSpringVolume)) { return false; } return prop.getMax();
} 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); }
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; 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; package emu.grasscutter.game.props;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static java.util.Map.entry;
import java.util.Arrays;
import java.util.stream.Stream; import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
...@@ -133,4 +139,65 @@ public enum FightProperty { ...@@ -133,4 +139,65 @@ public enum FightProperty {
public static FightProperty getPropByName(String name) { public static FightProperty getPropByName(String name) {
return stringMap.getOrDefault(name, FIGHT_PROP_NONE); 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; ...@@ -6,41 +6,42 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public enum PlayerProperty { public enum PlayerProperty {
PROP_EXP (1001), PROP_NONE (0),
PROP_EXP (1001, 0),
PROP_BREAK_LEVEL (1002), PROP_BREAK_LEVEL (1002),
PROP_SATIATION_VAL (1003), PROP_SATIATION_VAL (1003),
PROP_SATIATION_PENALTY_TIME (1004), PROP_SATIATION_PENALTY_TIME (1004),
PROP_LEVEL (4001), PROP_LEVEL (4001, 0, 90),
PROP_LAST_CHANGE_AVATAR_TIME (10001), 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_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), // Current volume of the Statue of the Seven [0, PROP_MAX_SPRING_VOLUME] 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), // Auto HP recovery when approaching the Statue of the Seven [0, 1] 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), // Auto HP recovery percentage [0, 100] PROP_SPRING_AUTO_USE_PERCENT (10005, 0, 100), // 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_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), PROP_IS_WEATHER_LOCKED (10007, 0, 1),
PROP_IS_GAME_TIME_LOCKED (10008), PROP_IS_GAME_TIME_LOCKED (10008, 0, 1),
PROP_IS_TRANSFERABLE (10009), PROP_IS_TRANSFERABLE (10009, 0, 1),
PROP_MAX_STAMINA (10010), // Maximum stamina of the player (0 - 24000) PROP_MAX_STAMINA (10010, 0, 24_000), // Maximum stamina of the player (0 - 24000)
PROP_CUR_PERSIST_STAMINA (10011), // Used stamina of the player (0 - PROP_MAX_STAMINA) PROP_CUR_PERSIST_STAMINA (10011, true), // Used stamina of the player (0 - PROP_MAX_STAMINA)
PROP_CUR_TEMPORARY_STAMINA (10012), PROP_CUR_TEMPORARY_STAMINA (10012),
PROP_PLAYER_LEVEL (10013), PROP_PLAYER_LEVEL (10013, 1, 60),
PROP_PLAYER_EXP (10014), PROP_PLAYER_EXP (10014),
PROP_PLAYER_HCOIN (10015), // Primogem (-inf, +inf) PROP_PLAYER_HCOIN (10015), // Primogem (-inf, +inf)
// It is known that Mihoyo will make Primogem negative in the cases that a player spends // 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. // his gems and then got a money refund, so negative is allowed.
PROP_PLAYER_SCOIN (10016), // Mora [0, +inf) PROP_PLAYER_SCOIN (10016, 0), // 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_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 if in quest or something that disables MP [0, 1] 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] PROP_PLAYER_WORLD_LEVEL (10019, 0, 8), // [0, 8]
PROP_PLAYER_RESIN (10020), // Original Resin [0, +inf) 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_HCOIN (10022),
PROP_PLAYER_WAIT_SUB_SCOIN (10023), 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_MCOIN (10025), // Genesis Crystal (-inf, +inf) see 10015
PROP_PLAYER_WAIT_SUB_MCOIN (10026), PROP_PLAYER_WAIT_SUB_MCOIN (10026),
PROP_PLAYER_LEGENDARY_KEY (10027), PROP_PLAYER_LEGENDARY_KEY (10027),
PROP_IS_HAS_FIRST_SHARE (10028), 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_METER (10035),
PROP_CUR_CLIMATE_TYPE (10036), PROP_CUR_CLIMATE_TYPE (10036),
PROP_CUR_CLIMATE_AREA_ID (10037), PROP_CUR_CLIMATE_AREA_ID (10037),
...@@ -48,22 +49,55 @@ public enum PlayerProperty { ...@@ -48,22 +49,55 @@ public enum PlayerProperty {
PROP_PLAYER_WORLD_LEVEL_LIMIT (10039), PROP_PLAYER_WORLD_LEVEL_LIMIT (10039),
PROP_PLAYER_WORLD_LEVEL_ADJUST_CD (10040), PROP_PLAYER_WORLD_LEVEL_ADJUST_CD (10040),
PROP_PLAYER_LEGENDARY_DAILY_TASK_NUM (10041), 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); 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<>(); private static final Int2ObjectMap<PlayerProperty> map = new Int2ObjectOpenHashMap<>();
static { static {
Stream.of(values()).forEach(e -> map.put(e.getId(), e)); 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.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() { 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) { public static PlayerProperty getPropById(int value) {
......
...@@ -9,6 +9,7 @@ import emu.grasscutter.server.game.GameServer; ...@@ -9,6 +9,7 @@ import emu.grasscutter.server.game.GameServer;
import java.io.FileReader; import java.io.FileReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static emu.grasscutter.Configuration.*; import static emu.grasscutter.Configuration.*;
...@@ -49,6 +50,12 @@ public class TowerScheduleManager { ...@@ -49,6 +50,12 @@ public class TowerScheduleManager {
return data; return data;
} }
public List<Integer> getAllFloors() {
List<Integer> floors = new ArrayList<>(this.getCurrentTowerScheduleData().getEntranceFloorId());
floors.addAll(this.getScheduleFloors());
return floors;
}
public List<Integer> getScheduleFloors() { public List<Integer> getScheduleFloors() {
return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList(); return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList();
} }
......
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