BattlePassManager.java 13.1 KB
Newer Older
1
2
package emu.grasscutter.game.battlepass;

3
4
5
6
7
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
8
9
10
11
12
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

13
14
15
16
17
18
import org.bson.types.ObjectId;

import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import dev.morphia.annotations.Transient;
19
import emu.grasscutter.GameConstants;
GanyusLeftHorn's avatar
GanyusLeftHorn committed
20
import emu.grasscutter.Grasscutter;
21
22
23
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.BattlePassRewardData;
24
import emu.grasscutter.data.excels.ItemData;
25
import emu.grasscutter.data.excels.RewardData;
26
import emu.grasscutter.database.DatabaseHelper;
27
28
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.MaterialType;
29
import emu.grasscutter.game.player.Player;
30
import emu.grasscutter.game.props.BattlePassMissionRefreshType;
31
32
33
34
35
36
import emu.grasscutter.game.props.BattlePassMissionStatus;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.BattlePassCycleOuterClass.BattlePassCycle;
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
import emu.grasscutter.net.proto.BattlePassRewardTakeOptionOuterClass.BattlePassRewardTakeOption;
import emu.grasscutter.net.proto.BattlePassScheduleOuterClass.BattlePassSchedule;
37
import emu.grasscutter.server.packet.send.PacketBattlePassCurScheduleUpdateNotify;
38
39
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
import emu.grasscutter.server.packet.send.PacketTakeBattlePassRewardRsp;
40

41
import lombok.Getter;
42

43
@Entity(value = "battlepass", useDiscriminator = false)
44
public class BattlePassManager {
45
46
	@Id @Getter private ObjectId id;
	@Transient @Getter private Player player;
47
48
	
	@Indexed private int ownerUid;
49
50
51
    @Getter private int point;
    @Getter private int cyclePoints; // Weekly maximum cap
    @Getter private int level;
52
    
53
    @Getter private boolean viewed;
GanyusLeftHorn's avatar
GanyusLeftHorn committed
54
    private boolean paid;
55
56
57
    
    private Map<Integer, BattlePassMission> missions;
    private Map<Integer, BattlePassReward> takenRewards;
58
59
60
    
    @Deprecated // Morphia only
    public BattlePassManager() {}
61

62
63
64
65
66
67
68
69
70
    public BattlePassManager(Player player) {
        this.setPlayer(player);
    }
    
    public void setPlayer(Player player) {
    	this.player = player;
    	this.ownerUid = player.getUid();
    }
    
71
72
    public void updateViewed() {
		this.viewed = true;
73
74
    }

75
76
77
78
79
80
81
82
	public boolean setLevel(int level) {
		if (level >= 0 && level <= GameConstants.BATTLE_PASS_MAX_LEVEL) {
			this.level = level;
			this.point = 0;
			this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.player));
			return true;
		}
		return false;
83
84
	}

85
86
	public void addPoints(int points){
        this.addPointsDirectly(points, false);
87
   
88
        this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(player));
89
        this.save();
90
91
    }

92
93
    public void addPointsDirectly(int points, boolean isWeekly) {
    	int amount = points;
94
95
96
97
98
99
100
101
102
103
104
    	
    	if (isWeekly) {
    		amount = Math.min(amount, GameConstants.BATTLE_PASS_POINT_PER_WEEK - this.cyclePoints);
    	}
    	
    	if (amount <= 0) {
    		return;
    	}
    	
        this.point += amount;
        this.cyclePoints += amount;
105
106
        
        if (this.point >= GameConstants.BATTLE_PASS_POINT_PER_LEVEL && this.getLevel() < GameConstants.BATTLE_PASS_MAX_LEVEL) {
107
        	int levelups = Math.floorDiv(this.point, GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
108
109
110
111
112
113
114
115
        	
        	// 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;
        }
116
    }
117
    
118
119
120
121
122
123
124
125
126
127
128
129
130
131
	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);
	}

GanyusLeftHorn's avatar
GanyusLeftHorn committed
132
133
134
135
136
	public boolean isPaid() {
		// ToDo: Change this when we actually support unlocking "paid" BP.
		return true;
	}

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
	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) {
175
				this.addPointsDirectly(mission.getData().getAddPoint(), mission.getData().isCycleRefresh());
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
				mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN);
				
				updatedMissions.add(mission);
			}
		}
		
		if (updatedMissions.size() > 0) {
			// Save to db
			this.save();
			
			// Packet
			getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions));
			getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
		}
	}
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

	private void takeRewardsFromSelectChest(ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
		// Sanity checks.
		if (rewardItemData.getItemUse().size() < 1) {
			return;
		}

		// Get possible item choices.
		String[] choices = rewardItemData.getItemUse().get(0).getUseParam().get(0).split(",");
		if (choices.length < index) {
			return;
		}

		// Get data for the selected item.
		// This depends on the type of chest.
		int chosenId = Integer.parseInt(choices[index - 1]);

		// For ITEM_USE_ADD_SELECT_ITEM chests, we can directly add the item specified in the chest's data.
		if (rewardItemData.getItemUse().get(0).getUseOp().equals("ITEM_USE_ADD_SELECT_ITEM")) {
			GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(chosenId), entry.getItemCount());
			rewardItems.add(rewardItem);
		}
		// For ITEM_USE_GRANT_SELECT_REWARD chests, we have to again look up reward data.
		else if (rewardItemData.getItemUse().get(0).getUseOp().equals("ITEM_USE_GRANT_SELECT_REWARD")) {
			RewardData selectedReward = GameData.getRewardDataMap().get(chosenId);

			for (var r : selectedReward.getRewardItemList()) {
				GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getItemCount());
				rewardItems.add(rewardItem);
			}
		}
		else {
			Grasscutter.getLogger().error("Invalid chest type for BP reward.");
		}
	}
226
227
	
	public void takeReward(List<BattlePassRewardTakeOption> takeOptionList) {
228
		List<BattlePassRewardTakeOption> rewardList = new ArrayList<>();
229
230
231
232
233
234
235
236
237
238
239
240
		
		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;
			}
			
241
			BattlePassRewardData rewardData = GameData.getBattlePassRewardDataMap().get(GameConstants.BATTLE_PASS_CURRENT_INDEX * 100 + option.getTag().getLevel());
242
243
244
			
			// Sanity check with excel data
			if (rewardData.getFreeRewardIdList().contains(option.getTag().getRewardId())) {
245
				rewardList.add(option);
246
			} else if (this.isPaid() && rewardData.getPaidRewardIdList().contains(option.getTag().getRewardId())) {
247
				rewardList.add(option);
248
			}
249
250
251
			else {
				Grasscutter.getLogger().info("Not in rewards list: {}", option.getTag().getRewardId());
			}
252
253
254
		}
		
		// Get rewards
255
		List<GameItem> rewardItems = null;
256
257
		
		if (rewardList.size() > 0) {
258

259
260
			rewardItems = new ArrayList<>();
			
261
262
263
264
265
			for (var option : rewardList) {
				var tag = option.getTag();
				int index = option.getOptionIdx();

				// Make sure we have reward data.
266
				RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId());
267
268
269
				if (reward == null) {
					continue;
				}
270
				
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
				// Add reward items.
				for (var entry : reward.getRewardItemList()) {
					ItemData rewardItemData = GameData.getItemDataMap().get(entry.getItemId());

					// Some rewards are chests where the user can select the item they want.
					if (rewardItemData.getMaterialType() == MaterialType.MATERIAL_SELECTABLE_CHEST) {
						this.takeRewardsFromSelectChest(rewardItemData, index, entry, rewardItems);
					}
					// All other rewards directly give us the right item.
					else {
						GameItem rewardItem = new GameItem(rewardItemData, entry.getItemCount());
						rewardItems.add(rewardItem);
					}
				}

				// Construct the reward and set as taken.
287
288
289
290
291
292
293
294
				BattlePassReward bpReward = new BattlePassReward(tag.getLevel(), tag.getRewardId(), tag.getUnlockStatus() == BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID);
				this.getTakenRewards().put(bpReward.getRewardId(), bpReward);
			}
			
			// Save to db
			this.save();
			
			// Add items and send battle pass schedule packet
295
			getPlayer().getInventory().addItems(rewardItems);
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
			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() {
322
323
324
325
326
327
328
329
330
331
332
333
334
		var resetMissions = new ArrayList<BattlePassMission>();

		for (var mission : this.missions.values()) {
			if (mission.getData().getRefreshType() == null || mission.getData().getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_DAILY) {
				mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
				mission.setProgress(0);

				resetMissions.add(mission);
			}
		}

		this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
		this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
335
336
337
	}
	
	public void resetWeeklyMissions() {
338
339
340
341
342
343
344
345
346
347
348
349
350
		var resetMissions = new ArrayList<BattlePassMission>();

		for (var mission : this.missions.values()) {
			if (mission.getData().getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE) {
				mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
				mission.setProgress(0);

				resetMissions.add(mission);
			}
		}

		this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
		this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
351
352
353
354
	}
	
	//
	public BattlePassSchedule getScheduleProto() {
GanyusLeftHorn's avatar
GanyusLeftHorn committed
355
356
357
358
		var currentDate = LocalDate.now();
		var nextSundayDate = (currentDate.getDayOfWeek() == DayOfWeek.SUNDAY) 
			? currentDate 
			: LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
359
360
		var nextSundayTime = LocalDateTime.of(nextSundayDate.getYear(), nextSundayDate.getMonthValue(), nextSundayDate.getDayOfMonth(), 23, 59, 59);
		
361
362
363
364
365
366
367
368
		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)
GanyusLeftHorn's avatar
GanyusLeftHorn committed
369
370
                .setJPFMGBEBBBJ(2) // Not bought on Playstation.
				.setCurCyclePoints(this.getCyclePoints())
371
372
373
374
375
                .setCurCycle(BattlePassCycle.newBuilder()
					.setBeginTime(0)
					.setEndTime((int)nextSundayTime.atZone(ZoneId.systemDefault()).toEpochSecond())
					.setCycleIdx(3)
				);
376
377
378
379
380
381
382
383
		
		for (BattlePassReward reward : getTakenRewards().values()) {
			schedule.addRewardTakenList(reward.toProto());
		}
		
		return schedule.build();
	}
	
384
385
    public void save() {
    	DatabaseHelper.saveBattlePass(this);
386
387
    }
}