From e8371677bdcb6477b1c2fd58b7a86feb78ac4731 Mon Sep 17 00:00:00 2001
From: AnimeGitB <AnimeGitB@bigblueball.in>
Date: Mon, 4 Jul 2022 01:27:50 +0930
Subject: [PATCH] Add gachaTimesLimit for beginner banner Also add some more
 return codes

---
 .../grasscutter/game/gacha/GachaBanner.java   | 100 +++++-------------
 .../grasscutter/game/gacha/GachaManager.java  |  19 +++-
 .../game/gacha/PlayerGachaBannerInfo.java     |  49 +++------
 .../server/packet/send/PacketDoGachaRsp.java  |   8 +-
 4 files changed, 60 insertions(+), 116 deletions(-)

diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
index 9286c23e..dd68ad4a 100644
--- a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
+++ b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
@@ -1,77 +1,51 @@
 package emu.grasscutter.game.gacha;
 
-import emu.grasscutter.database.DatabaseHelper;
-import emu.grasscutter.game.Account;
+import emu.grasscutter.data.common.ItemParamData;
 import emu.grasscutter.game.player.Player;
 import emu.grasscutter.net.proto.GachaInfoOuterClass.GachaInfo;
 import emu.grasscutter.net.proto.GachaUpInfoOuterClass.GachaUpInfo;
 import emu.grasscutter.utils.Utils;
+import lombok.Getter;
 
 import static emu.grasscutter.Configuration.*;
 
-import emu.grasscutter.Grasscutter;
-import emu.grasscutter.data.common.ItemParamData;
-
 public class GachaBanner {
-	private int gachaType;
-	private int scheduleId;
-	private String prefabPath;
-	private String previewPrefabPath;
-	private String titlePath;
+	@Getter private int gachaType;
+	@Getter private int scheduleId;
+	@Getter private String prefabPath;
+	@Getter private String previewPrefabPath;
+	@Getter private String titlePath;
 	private int costItemId = 0;
 	private int costItemAmount = 1;
 	private int costItemId10 = 0;
 	private int costItemAmount10 = 10;
-	private int beginTime;
-	private int endTime;
-	private int sortId;
+	@Getter private int beginTime;
+	@Getter private int endTime;
+	@Getter private int sortId;
+	@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
 	private int[] rateUpItems4 = {};
 	private int[] rateUpItems5 = {};
-	private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
-	private int[] fallbackItems4Pool1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064};
-	private int[] fallbackItems4Pool2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
-	private int[] fallbackItems5Pool1 = {1003, 1016, 1042, 1035, 1041};
-	private int[] fallbackItems5Pool2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
-	private boolean removeC6FromPool = false;
-	private boolean autoStripRateUpFromFallback = true;
+	@Getter private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
+	@Getter private int[] fallbackItems4Pool1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064};
+	@Getter private int[] fallbackItems4Pool2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
+	@Getter private int[] fallbackItems5Pool1 = {1003, 1016, 1042, 1035, 1041};
+	@Getter private int[] fallbackItems5Pool2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
+	@Getter private boolean removeC6FromPool = false;
+	@Getter private boolean autoStripRateUpFromFallback = true;
 	private int[][] weights4 = {{1,510}, {8,510}, {10,10000}};
 	private int[][] weights5 = {{1,75}, {73,150}, {90,10000}};
 	private int[][] poolBalanceWeights4 = {{1,255}, {17,255}, {21,10455}};
 	private int[][] poolBalanceWeights5 = {{1,30}, {147,150}, {181,10230}};
 	private int eventChance4 = 50; // Chance to win a featured event item
 	private int eventChance5 = 50; // Chance to win a featured event item
-	private BannerType bannerType = BannerType.STANDARD;
+	@Getter private BannerType bannerType = BannerType.STANDARD;
 
 	// Kinda wanna deprecate these but they're in people's configs
 	private int[] rateUpItems1 = {};
 	private int[] rateUpItems2 = {};
 	private int eventChance = -1;
 	private int costItem = 0;
-	private int wishMaxProgress = 2;
-	
-	public int getGachaType() {
-		return gachaType;
-	}
-
-	public BannerType getBannerType() {
-		return bannerType;
-	}
-
-	public int getScheduleId() {
-		return scheduleId;
-	}
-
-	public String getPrefabPath() {
-		return prefabPath;
-	}
-
-	public String getPreviewPrefabPath() {
-		return previewPrefabPath;
-	}
-
-	public String getTitlePath() {
-		return titlePath;
-	}
+	@Getter private int wishMaxProgress = 2;
 
 	public ItemParamData getCost(int numRolls) {
 		return switch (numRolls) {
@@ -84,18 +58,6 @@ public class GachaBanner {
 		return (costItem > 0) ? costItem : costItemId;
 	}
 
-	public int getBeginTime() {
-		return beginTime;
-	}
-
-	public int getEndTime() {
-		return endTime;
-	}
-
-	public int getSortId() {
-		return sortId;
-	}
-
 	public int[] getRateUpItems4() {
 		return (rateUpItems2.length > 0) ? rateUpItems2 : rateUpItems4;
 	}
@@ -103,17 +65,6 @@ public class GachaBanner {
 		return (rateUpItems1.length > 0) ? rateUpItems1 : rateUpItems5;
 	}
 
-	public int[] getFallbackItems3() {return fallbackItems3;}
-	public int[] getFallbackItems4Pool1() {return fallbackItems4Pool1;}
-	public int[] getFallbackItems4Pool2() {return fallbackItems4Pool2;}
-	public int[] getFallbackItems5Pool1() {return fallbackItems5Pool1;}
-	public int[] getFallbackItems5Pool2() {return fallbackItems5Pool2;}
-
-	public boolean getRemoveC6FromPool() {return removeC6FromPool;}
-	public boolean getAutoStripRateUpFromFallback() {return autoStripRateUpFromFallback;}
-
-	public int getWishMaxProgress() {return wishMaxProgress;}
-
 	public boolean hasEpitomized() {
 		return bannerType.equals(BannerType.WEAPON);
 	}
@@ -155,6 +106,11 @@ public class GachaBanner {
 		// Grasscutter.getLogger().info("record = " + record);
 		ItemParamData costItem1 = this.getCost(1);
 		ItemParamData costItem10 = this.getCost(10);
+		PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
+		int leftGachaTimes = switch(gachaTimesLimit) {
+			case Integer.MAX_VALUE -> Integer.MAX_VALUE;
+			default -> Math.max(gachaTimesLimit - gachaInfo.getTotalPulls(), 0);
+		};
 		GachaInfo.Builder info = GachaInfo.newBuilder()
 				.setGachaType(this.getGachaType())
 				.setScheduleId(this.getScheduleId())
@@ -170,13 +126,11 @@ public class GachaBanner {
 	            .setGachaProbUrlOversea(details)
 	            .setGachaRecordUrl(record)
 	            .setGachaRecordUrlOversea(record)
-	            .setLeftGachaTimes(Integer.MAX_VALUE)
-	            .setGachaTimesLimit(Integer.MAX_VALUE)
+	            .setLeftGachaTimes(leftGachaTimes)
+	            .setGachaTimesLimit(gachaTimesLimit)
 	            .setGachaSortId(this.getSortId());
 
 		if(hasEpitomized()) {
-			PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
-
 			info.setWishItemId(gachaInfo.getWishItemId())
 				.setWishProgress(gachaInfo.getFailedChosenItemPulls())
 				.setWishMaxProgress(this.getWishMaxProgress());
diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java
index b91d09a3..661bfca5 100644
--- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java
+++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java
@@ -113,7 +113,7 @@ public class GachaManager {
 			fallbackItems5Pool1 = banner.getFallbackItems5Pool1();
 			fallbackItems5Pool2 = banner.getFallbackItems5Pool2();
 
-			if (banner.getAutoStripRateUpFromFallback()) {
+			if (banner.isAutoStripRateUpFromFallback()) {
 				fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, rateUpItems4);
 				fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, rateUpItems4);
 				fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, rateUpItems5);
@@ -260,6 +260,7 @@ public class GachaManager {
 	public synchronized void doPulls(Player player, int scheduleId, int times) {
 		// Sanity check
 		if (times != 10 && times != 1) {
+			player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_INVALID_TIMES));
 			return;
 		}
 		Inventory inventory = player.getInventory();
@@ -275,20 +276,28 @@ public class GachaManager {
 			return;
 		}
 
+		// Check against total limit
+		PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner);
+		int gachaTimesLimit = banner.getGachaTimesLimit();
+		if (gachaTimesLimit != Integer.MAX_VALUE && (gachaInfo.getTotalPulls() + times) > gachaTimesLimit) {
+			player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_TIMES_LIMIT));
+			return;
+		}
+
 		// Spend currency
 		ItemParamData cost = banner.getCost(times);
 		if (cost.getCount() > 0 && !inventory.payItem(cost)) {
-			player.sendPacket(new PacketDoGachaRsp());
+			player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_COST_ITEM_NOT_ENOUGH));
 			return;
 		}
 
 		// Add to character
-		PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner);
+		gachaInfo.addTotalPulls(times);
 		BannerPools pools = new BannerPools(banner);
 		List<GachaItem> list = new ArrayList<>();
 		int stardust = 0, starglitter = 0;
 
-		if (banner.getRemoveC6FromPool()) {  // The ultimate form of pity (non-vanilla)
+		if (banner.isRemoveC6FromPool()) {  // The ultimate form of pity (non-vanilla)
 			pools.rateUpItems4 = removeC6FromPool(pools.rateUpItems4, player);
 			pools.rateUpItems5 = removeC6FromPool(pools.rateUpItems5, player);
 			pools.fallbackItems4Pool1 = removeC6FromPool(pools.fallbackItems4Pool1, player);
@@ -331,7 +340,7 @@ public class GachaManager {
 					if (constellation >= 6) {  // C6, give consolation starglitter
 						addStarglitter = (itemData.getRankLevel()==5)? 25 : 5;
 					} else {  // C0-C5, give constellation item
-						if (banner.getRemoveC6FromPool() && constellation == 5) {  // New C6, remove it from the pools so we don't get C7 in a 10pull
+						if (banner.isRemoveC6FromPool() && constellation == 5) {  // New C6, remove it from the pools so we don't get C7 in a 10pull
 							pools.removeFromAllPools(new int[] {itemId});
 						}
 						addStarglitter = (itemData.getRankLevel()==5)? 10 : 2;
diff --git a/src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java b/src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java
index 00b07ed9..baac5774 100644
--- a/src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java
+++ b/src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java
@@ -1,11 +1,14 @@
 package emu.grasscutter.game.gacha;
 
 import dev.morphia.annotations.Entity;
+import lombok.Getter;
+import lombok.Setter;
 
 @Entity
 public class PlayerGachaBannerInfo {
-	private int pity5 = 0;
-	private int pity4 = 0;
+	@Getter @Setter private int totalPulls = 0;
+	@Getter @Setter private int pity5 = 0;
+	@Getter @Setter private int pity4 = 0;
 	private int failedFeaturedItemPulls = 0;
 	private int failedFeatured4ItemPulls = 0;
 	private int pity5Pool1 = 0;
@@ -13,49 +16,21 @@ public class PlayerGachaBannerInfo {
 	private int pity4Pool1 = 0;
 	private int pity4Pool2 = 0;
 
-	private int failedChosenItemPulls = 0;
-	private int wishItemId = 0;
-	
-	public int getPity5() {
-		return pity5;
-	}
-	
-	public void setPity5(int pity5) {
-		this.pity5 = pity5;
+	@Getter @Setter private int failedChosenItemPulls = 0;
+	@Getter @Setter private int wishItemId = 0;
+
+	public void addTotalPulls(int amount) {
+		this.totalPulls += amount;
 	}
-	
+
 	public void addPity5(int amount) {
 		this.pity5 += amount;
 	}
-	
-	public int getPity4() {
-		return pity4;
-	}
-	
-	public void setPity4(int pity4) {
-		this.pity4 = pity4;
-	}
-	
+
 	public void addPity4(int amount) {
 		this.pity4 += amount;
 	}
 
-	public int getWishItemId() {
-		return wishItemId;
-	}
-
-	public void setWishItemId(int wishItemId) {
-		this.wishItemId = wishItemId;
-	}
-
-	public int getFailedChosenItemPulls() {
-		return failedChosenItemPulls;
-	}
-
-	public void setFailedChosenItemPulls(int amount) {
-		failedChosenItemPulls = amount;
-	}
-
 	public void addFailedChosenItemPulls(int amount) {
 		failedChosenItemPulls += amount;
 	}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDoGachaRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDoGachaRsp.java
index f63e053b..c7f8fc6e 100644
--- a/src/main/java/emu/grasscutter/server/packet/send/PacketDoGachaRsp.java
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDoGachaRsp.java
@@ -19,12 +19,18 @@ public class PacketDoGachaRsp extends BasePacket {
 
 		ItemParamData costItem = banner.getCost(1);
 		ItemParamData costItem10 = banner.getCost(10);
+		int gachaTimesLimit = banner.getGachaTimesLimit();
+		int leftGachaTimes = switch(gachaTimesLimit) {
+			case Integer.MAX_VALUE -> Integer.MAX_VALUE;
+			default -> Math.max(gachaTimesLimit - gachaInfo.getTotalPulls(), 0);
+		};
 		DoGachaRsp.Builder rsp = DoGachaRsp.newBuilder()
 				.setGachaType(banner.getGachaType())
 				.setGachaScheduleId(banner.getScheduleId())
 				.setGachaTimes(list.size())
 				.setNewGachaRandom(12345)
-				.setLeftGachaTimes(Integer.MAX_VALUE)
+				.setLeftGachaTimes(leftGachaTimes)
+	            .setGachaTimesLimit(gachaTimesLimit)
 				.setCostItemId(costItem.getId())
 	            .setCostItemNum(costItem.getCount())
 	            .setTenCostItemId(costItem10.getId())
-- 
GitLab