Commit 5eb1d34b authored by Melledy's avatar Melledy
Browse files

Implement battle pass triggers/rewards

parent 34f7c6e7
...@@ -18,6 +18,10 @@ public final class GameConstants { ...@@ -18,6 +18,10 @@ public final class GameConstants {
public static final int SERVER_CONSOLE_UID = 99; // The UID of the server console's "player". public static final int SERVER_CONSOLE_UID = 99; // The UID of the server console's "player".
public static final int BATTLE_PASS_MAX_LEVEL = 50;
public static final int BATTLE_PASS_POINT_PER_LEVEL = 1000;
public static final int BATTLE_PASS_LEVEL_PRICE = 150;
// Default entity ability hashes. // Default entity ability hashes.
public static final String[] DEFAULT_ABILITY_STRINGS = { public static final String[] DEFAULT_ABILITY_STRINGS = {
"Avatar_DefaultAbility_VisionReplaceDieInvincible", "Avatar_DefaultAbility_AvartarInShaderChange", "Avatar_SprintBS_Invincible", "Avatar_DefaultAbility_VisionReplaceDieInvincible", "Avatar_DefaultAbility_AvartarInShaderChange", "Avatar_SprintBS_Invincible",
......
...@@ -17,7 +17,6 @@ public final class SetBPLevelCommand implements CommandHandler { ...@@ -17,7 +17,6 @@ public final class SetBPLevelCommand implements CommandHandler {
int level = Integer.parseInt(args.get(0)); int level = Integer.parseInt(args.get(0));
sender.getBattlePassManager().addPoint(level); sender.getBattlePassManager().addPoints(level);
sender.getBattlePassManager().updateAwardTakenLevel(0);
} }
} }
...@@ -95,8 +95,8 @@ public class GameData { ...@@ -95,8 +95,8 @@ public class GameData {
private static final Int2ObjectMap<InvestigationMonsterData> investigationMonsterDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<InvestigationMonsterData> investigationMonsterDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CityData> cityDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<CityData> cityDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<WeatherData> weatherDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<WeatherData> weatherDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<BattlePassMissionExcelConfigData> battlePassMissionExcelConfigDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<BattlePassMissionData> battlePassMissionDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<BattlePassRewardExcelConfigData> battlePassRewardExcelConfigDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<BattlePassRewardData> battlePassRewardDataMap = new Int2ObjectOpenHashMap<>();
// Cache // Cache
private static Map<Integer, List<Integer>> fetters = new HashMap<>(); private static Map<Integer, List<Integer>> fetters = new HashMap<>();
...@@ -424,11 +424,11 @@ public class GameData { ...@@ -424,11 +424,11 @@ public class GameData {
return weatherDataMap; return weatherDataMap;
} }
public static Int2ObjectMap<BattlePassMissionExcelConfigData> getBattlePassMissionExcelConfigDataMap() { public static Int2ObjectMap<BattlePassMissionData> getBattlePassMissionDataMap() {
return battlePassMissionExcelConfigDataMap; return battlePassMissionDataMap;
} }
public static Int2ObjectMap<BattlePassRewardExcelConfigData> getBattlePassRewardExcelConfigDataMap() { public static Int2ObjectMap<BattlePassRewardData> getBattlePassRewardDataMap() {
return battlePassRewardExcelConfigDataMap; return battlePassRewardDataMap;
} }
} }
package emu.grasscutter.data.excels;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.BattlePassMissionRefreshType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
@ResourceType(name = {"BattlePassMissionExcelConfigData.json"})
@Getter
public class BattlePassMissionData extends GameResource {
private int addPoint;
private int id;
private int scheduleId;
private int progress;
private TriggerConfig triggerConfig;
private BattlePassMissionRefreshType refreshType;
private transient Set<Integer> mainParams;
@Override
public int getId() {
return this.id;
}
public WatcherTriggerType getTriggerType() {
return this.getTriggerConfig().getTriggerType();
}
public boolean isValidRefreshType() {
return getRefreshType() == null ||
getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE ||
getScheduleId() == 2701;
}
@Override
public void onLoad() {
if (this.getTriggerConfig() != null && getTriggerConfig().getParamList()[0].length() > 0) {
this.mainParams = Arrays.stream(getTriggerConfig().getParamList()[0].split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet());
}
}
@Getter
public static class TriggerConfig {
private WatcherTriggerType triggerType;
private String[] paramList;
}
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
var protoBuilder = emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
protoBuilder
.setMissionId(getId())
.setTotalProgress(this.getProgress())
.setRewardBattlePassPoint(this.getAddPoint())
.setMissionStatus(MissionStatus.MISSION_STATUS_UNFINISHED)
.setMissionType(this.getRefreshType() == null ? 0 : this.getRefreshType().getValue());
return protoBuilder.build();
}
}
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
@ResourceType(name = {"BattlePassMissionExcelConfigData.json"})
@FieldDefaults(level = AccessLevel.PRIVATE)
@Getter
@Setter
public class BattlePassMissionExcelConfigData extends GameResource {
private int addPoint;
private int id;
private int progress;
private String refreshType;
@Override
public void onLoad() {
}
@Override
public int getId() {
return this.id;
}
}
...@@ -9,8 +9,7 @@ import java.util.List; ...@@ -9,8 +9,7 @@ import java.util.List;
@ResourceType(name = "BattlePassRewardExcelConfigData.json") @ResourceType(name = "BattlePassRewardExcelConfigData.json")
@Getter @Getter
@Setter public class BattlePassRewardData extends GameResource {
public class BattlePassRewardExcelConfigData extends GameResource {
private int indexId; private int indexId;
private int level; private int level;
private List<Integer> freeRewardIdList; private List<Integer> freeRewardIdList;
...@@ -23,5 +22,6 @@ public class BattlePassRewardExcelConfigData extends GameResource { ...@@ -23,5 +22,6 @@ public class BattlePassRewardExcelConfigData extends GameResource {
@Override @Override
public void onLoad() { public void onLoad() {
} }
} }
...@@ -131,6 +131,7 @@ public final class DatabaseHelper { ...@@ -131,6 +131,7 @@ public final class DatabaseHelper {
DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", player.getUid())); DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", player.getUid()));
DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", player.getUid())); DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", player.getUid()));
DatabaseManager.getGameDatabase().getCollection("quests").deleteMany(eq("ownerUid", player.getUid())); DatabaseManager.getGameDatabase().getCollection("quests").deleteMany(eq("ownerUid", player.getUid()));
DatabaseManager.getGameDatabase().getCollection("battlepass").deleteMany(eq("ownerUid", player.getUid()));
// Delete friendships. // Delete friendships.
// Here, we need to make sure to not only delete the deleted account's friendships, // Here, we need to make sure to not only delete the deleted account's friendships,
......
package emu.grasscutter.game.battlepass; package emu.grasscutter.game.battlepass;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed; import dev.morphia.annotations.Indexed;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.GameConstants;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.BattlePassRewardData;
import emu.grasscutter.data.excels.RewardData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.BattlePassMissionStatus;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.BattlePassCycleOuterClass.BattlePassCycle;
import emu.grasscutter.net.proto.BattlePassRewardTagOuterClass.BattlePassRewardTag;
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
import emu.grasscutter.net.proto.BattlePassRewardTakeOptionOuterClass.BattlePassRewardTakeOption;
import emu.grasscutter.net.proto.BattlePassScheduleOuterClass.BattlePassSchedule;
import emu.grasscutter.server.packet.send.PacketBattlePassCurScheduleUpdateNotify; import emu.grasscutter.server.packet.send.PacketBattlePassCurScheduleUpdateNotify;
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
import emu.grasscutter.server.packet.send.PacketTakeBattlePassRewardRsp;
@Entity(value = "battlepass", useDiscriminator = false) @Entity(value = "battlepass", useDiscriminator = false)
public class BattlePassManager { public class BattlePassManager {
...@@ -17,7 +37,13 @@ public class BattlePassManager { ...@@ -17,7 +37,13 @@ public class BattlePassManager {
@Indexed private int ownerUid; @Indexed private int ownerUid;
private int point; private int point;
private int awardTakenLevel; private int level;
private boolean viewed;
private boolean paid;
private Map<Integer, BattlePassMission> missions;
private Map<Integer, BattlePassReward> takenRewards;
@Deprecated // Morphia only @Deprecated // Morphia only
public BattlePassManager() {} public BattlePassManager() {}
...@@ -40,23 +66,213 @@ public class BattlePassManager { ...@@ -40,23 +66,213 @@ public class BattlePassManager {
} }
public int getPoint() { public int getPoint() {
return point; return this.point;
} }
public int getAwardTakenLevel() { public int getLevel() {
return awardTakenLevel; return this.level;
} }
public void addPoint(int point){ public boolean isViewed() {
this.point += point; return viewed;
}
public void updateViewed() {
this.viewed = true;
}
public boolean isPaid() {
return paid;
}
public void addPoints(int point){
this.addPointsDirectly(point);
player.getSession().send(new PacketBattlePassCurScheduleUpdateNotify(player.getSession().getPlayer())); player.getSession().send(new PacketBattlePassCurScheduleUpdateNotify(player.getSession().getPlayer()));
this.save(); this.save();
} }
public void updateAwardTakenLevel(int level){ public void addPointsDirectly(int point) {
this.awardTakenLevel = level; this.point += point;
player.getSession().send(new PacketBattlePassCurScheduleUpdateNotify(player.getSession().getPlayer()));
if (this.point >= GameConstants.BATTLE_PASS_POINT_PER_LEVEL && this.getLevel() < GameConstants.BATTLE_PASS_MAX_LEVEL) {
int levelups = (int) Math.floor((float) this.point / GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
// Make sure player cant go above max BP level
levelups = Math.min(levelups, GameConstants.BATTLE_PASS_MAX_LEVEL - levelups);
// Set new points after level up
this.point = this.point - (levelups * GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
this.level += levelups;
}
}
public Map<Integer, BattlePassMission> getMissions() {
if (this.missions == null) this.missions = new HashMap<>();
return this.missions;
}
// Will return a new empty mission if the mission id is not found
public BattlePassMission loadMissionById(int id) {
return getMissions().computeIfAbsent(id, i -> new BattlePassMission(i));
}
public boolean hasMission(int id) {
return getMissions().containsKey(id);
}
public Map<Integer, BattlePassReward> getTakenRewards() {
if (this.takenRewards == null) this.takenRewards = new HashMap<>();
return this.takenRewards;
}
// Mission triggers
public void triggerMission(WatcherTriggerType triggerType) {
getPlayer().getServer().getBattlePassMissionManager().triggerMission(getPlayer(), triggerType);
}
public void triggerMission(WatcherTriggerType triggerType, int param, int progress) {
getPlayer().getServer().getBattlePassMissionManager().triggerMission(getPlayer(), triggerType, param, progress);
}
// Handlers
public void takeMissionPoint(List<Integer> missionIdList) {
// Obvious exploit check
if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) {
return;
}
List<BattlePassMission> updatedMissions = new ArrayList<>(missionIdList.size());
for (int id : missionIdList) {
// Skip if we dont have this mission
if (!this.hasMission(id)) {
continue;
}
BattlePassMission mission = this.loadMissionById(id);
if (mission.getData() == null) {
this.getMissions().remove(mission.getId());
continue;
}
// Take reward
if (mission.getStatus() == BattlePassMissionStatus.MISSION_STATUS_FINISHED) {
this.addPointsDirectly(mission.getData().getAddPoint());
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN);
updatedMissions.add(mission);
}
}
if (updatedMissions.size() > 0) {
// Save to db
this.save(); this.save();
// Packet
getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions));
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
}
}
public void takeReward(List<BattlePassRewardTakeOption> takeOptionList) {
List<BattlePassRewardTag> rewardList = new ArrayList<>();
for (BattlePassRewardTakeOption option : takeOptionList) {
// Duplicate check
if (option.getTag().getRewardId() == 0 || getTakenRewards().containsKey(option.getTag().getRewardId())) {
continue;
}
// Level check
if (option.getTag().getLevel() > this.getLevel()) {
continue;
}
BattlePassRewardData rewardData = GameData.getBattlePassRewardDataMap().get(option.getTag().getLevel());
// Sanity check with excel data
if (rewardData.getFreeRewardIdList().contains(option.getTag().getRewardId())) {
rewardList.add(option.getTag());
} else if (this.isPaid() && rewardData.getPaidRewardIdList().contains(option.getTag().getRewardId())) {
rewardList.add(option.getTag());
}
}
// Get rewards
List<ItemParamData> rewardItems = null;
if (rewardList.size() > 0) {
rewardItems = new ArrayList<>();
for (BattlePassRewardTag tag : rewardList) {
RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId());
if (reward == null) continue;
BattlePassReward bpReward = new BattlePassReward(tag.getLevel(), tag.getRewardId(), tag.getUnlockStatus() == BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID);
this.getTakenRewards().put(bpReward.getRewardId(), bpReward);
rewardItems.addAll(reward.getRewardItemList());
}
// Save to db
this.save();
// Add items and send battle pass schedule packet
getPlayer().getInventory().addItemParamDatas(rewardItems);
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
}
getPlayer().sendPacket(new PacketTakeBattlePassRewardRsp(takeOptionList, rewardItems));
}
public int buyLevels(int buyLevel) {
int boughtLevels = Math.min(buyLevel, GameConstants.BATTLE_PASS_MAX_LEVEL - buyLevel);
if (boughtLevels > 0) {
int price = GameConstants.BATTLE_PASS_LEVEL_PRICE * boughtLevels;
if (getPlayer().getPrimogems() < price) {
return 0;
}
this.level += boughtLevels;
this.save();
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
}
return boughtLevels;
}
public void resetDailyMissions() {
// TODO
}
public void resetWeeklyMissions() {
// TODO
}
//
public BattlePassSchedule getScheduleProto() {
BattlePassSchedule.Builder schedule = BattlePassSchedule.newBuilder()
.setScheduleId(2700)
.setLevel(this.getLevel())
.setPoint(this.getPoint())
.setBeginTime(0)
.setEndTime(2059483200)
.setIsViewed(this.isViewed())
.setUnlockStatus(this.isPaid() ? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID : BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE)
.setCurCyclePoints(0)
.setCurCycle(BattlePassCycle.newBuilder().setBeginTime(0).setEndTime(2059483200).setCycleIdx(3));
for (BattlePassReward reward : getTakenRewards().values()) {
schedule.addRewardTakenList(reward.toProto());
}
return schedule.build();
} }
public void save() { public void save() {
......
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.game.props.BattlePassMissionStatus;
@Entity
public class BattlePassMission {
private int id;
private int progress;
private BattlePassMissionStatus status;
@Transient
private BattlePassMissionData data;
@Deprecated // Morphia only
public BattlePassMission() {}
public BattlePassMission(int id) {
this.id = id;
}
public int getId() {
return id;
}
public BattlePassMissionData getData() {
if (this.data == null) {
this.data = GameData.getBattlePassMissionDataMap().get(getId());
}
return this.data;
}
public int getProgress() {
return progress;
}
public void addProgress(int addProgress, int maxProgress) {
this.progress = Math.min(addProgress + this.progress, maxProgress);
}
public BattlePassMissionStatus getStatus() {
if (status == null) status = BattlePassMissionStatus.MISSION_STATUS_UNFINISHED;
return status;
}
public void setStatus(BattlePassMissionStatus status) {
this.status = status;
}
public boolean isFinshed() {
return getStatus().getValue() >= 2;
}
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
var protoBuilder = emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
protoBuilder
.setMissionId(getId())
.setCurProgress(getProgress())
.setTotalProgress(getData().getProgress())
.setRewardBattlePassPoint(getData().getAddPoint())
.setMissionStatus(getStatus().getMissionStatus())
.setMissionType(getData().getRefreshType() == null ? 0 : getData().getRefreshType().getValue());
return protoBuilder.build();
}
}
package emu.grasscutter.game.battlepass;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.BattlePassMissionData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.BattlePassMissionRefreshType;
import emu.grasscutter.game.props.BattlePassMissionStatus;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
public class BattlePassMissionManager {
private final GameServer server;
private final Map<WatcherTriggerType, List<BattlePassMissionData>> cachedTriggers;
// BP Mission manager for the server, contains cached triggers so we dont have to load it for each player
public BattlePassMissionManager(GameServer server) {
this.server = server;
this.cachedTriggers = new HashMap<>();
for (BattlePassMissionData missionData : GameData.getBattlePassMissionDataMap().values()) {
if (missionData.isValidRefreshType()) {
List<BattlePassMissionData> triggerList = getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>());
triggerList.add(missionData);
}
}
}
public GameServer getServer() {
return server;
}
private Map<WatcherTriggerType, List<BattlePassMissionData>> getTriggers() {
return cachedTriggers;
}
public void triggerMission(Player player, WatcherTriggerType triggerType) {
triggerMission(player, triggerType, 0, 1);
}
public void triggerMission(Player player, WatcherTriggerType triggerType, int param, int progress) {
List<BattlePassMissionData> triggerList = getTriggers().get(triggerType);
if (triggerList == null || triggerList.isEmpty()) return;
for (BattlePassMissionData data : triggerList) {
// Skip params check if param == 0
if (param != 0) {
if (!data.getMainParams().contains(param)) {
continue;
}
}
// Get mission from player, if it doesnt exist, then we make one
BattlePassMission mission = player.getBattlePassManager().loadMissionById(data.getId());
if (mission.isFinshed()) continue;
// Add progress
mission.addProgress(progress, data.getProgress());
if (mission.getProgress() >= data.getProgress()) {
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_FINISHED);
}
// Save to db
player.getBattlePassManager().save();
// Packet
player.sendPacket(new PacketBattlePassMissionUpdateNotify(mission));
}
}
}
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;
...@@ -375,6 +376,9 @@ public class GachaManager { ...@@ -375,6 +376,9 @@ public class GachaManager {
// 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) {
......
...@@ -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);
} }
} }
...@@ -389,6 +393,10 @@ public class Inventory implements Iterable<GameItem> { ...@@ -389,6 +393,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();
......
...@@ -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;
...@@ -46,6 +47,9 @@ public class ResinManager { ...@@ -46,6 +47,9 @@ public class ResinManager {
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN)); 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;
} }
......
...@@ -16,6 +16,7 @@ import emu.grasscutter.game.inventory.GameItem; ...@@ -16,6 +16,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.net.proto.ForgeStartReqOuterClass; import emu.grasscutter.net.proto.ForgeStartReqOuterClass;
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;
...@@ -196,6 +197,9 @@ public class ForgingManager { ...@@ -196,6 +197,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) {
ActiveForgeData remainingForge = new ActiveForgeData(); ActiveForgeData remainingForge = new ActiveForgeData();
......
...@@ -42,6 +42,7 @@ import emu.grasscutter.game.props.ActionReason; ...@@ -42,6 +42,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;
...@@ -1268,6 +1269,7 @@ public class Player { ...@@ -1268,6 +1269,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() {
...@@ -1425,6 +1427,9 @@ public class Player { ...@@ -1425,6 +1427,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());
......
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;
}
}
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