Files
Campofinale/Campofinale/Game/Gacha/GachaManager.cs
AlessandroCH 75dafc17b2 fix
2025-03-16 20:53:37 +01:00

258 lines
9.7 KiB
C#

using Campofinale.Database;
using Campofinale.Protocol;
using Campofinale.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static Campofinale.Resource.ResourceManager;
namespace Campofinale.Game.Gacha
{
public class GachaManager
{
public Player player;
internal ulong upSeqId;
const double fiftyfifty = 0.50;
private static readonly Random random = new Random();
public GachaManager(Player player)
{
this.player = player;
}
public (int fiveStarPity, int sixStarPity, GachaTransaction? lastSixStar, bool isFiftyFiftyLost) GetCurrentPity(string templateId)
{
List<GachaTransaction> transactionList = DatabaseManager.db.LoadGachaTransaction(player.roleId, templateId);
transactionList = transactionList.OrderBy(g => g.transactionTime).ToList();
int fiveStarPity = 0;
int sixStarPity = 0;
GachaTransaction? lastSixStar = null;
foreach (var transaction in transactionList)
{
if (transaction.rarity == 5)
{
fiveStarPity = 0;
sixStarPity++;
}
else if (transaction.rarity == 6)
{
fiveStarPity = 0;
sixStarPity = 0;
lastSixStar = transaction;
}
else
{
fiveStarPity++;
sixStarPity++;
}
//Logger.Print("Current calculated: " + sixStarPity);
}
bool isFiftyFiftyLost = false;
if (lastSixStar != null)
{
isFiftyFiftyLost = lastSixStar.itemId != templateId;
}
return (fiveStarPity, sixStarPity, lastSixStar, isFiftyFiftyLost);
}
public void DoGacha(string gachaId,int attempts)
{
const double prob6Star = 0.008; // 0.8%
const double prob5Star = 0.08; // 8%
(int fiveStarPity, int sixStarPity, GachaTransaction? lastSixStar, bool isFiftyFiftyLost)
PityInfo = GetCurrentPity(gachaId);
int increaseTime = 0;
int pityforcalculate = PityInfo.sixStarPity-64;
if(pityforcalculate < 1)
{
pityforcalculate = 0;
}
GachaCharPoolTable table = ResourceManager.gachaCharPoolTable[gachaId];
GachaCharPoolContentTable content = ResourceManager.gachaCharPoolContentTable[gachaId];
GachaCharPoolTypeTable type = ResourceManager.gachaCharPoolTypeTable[""+table.type];
//Sanity check
if (table==null || content == null || type==null)
{
return;
}
List<GachaCharPoolItem> fiveStars = content.list.FindAll(c => c.starLevel == 5);
List<GachaCharPoolItem> sixStars = content.list.FindAll(c => c.starLevel == 6);
List<GachaCharPoolItem> fourStars = content.list.FindAll(c => c.starLevel == 4);
List<GachaTransaction> transactions = new();
for (int i = 0; i < attempts; i++)
{
double roll = random.NextDouble();
double fifty = random.NextDouble();
double finalProb6Star = prob6Star + 0.05f * pityforcalculate;
PityInfo.fiveStarPity++;
PityInfo.sixStarPity++;
GachaTransaction transaction = null;
//Six star pull
if (roll < finalProb6Star || PityInfo.sixStarPity>=type.softGuarantee)
{
PityInfo.sixStarPity -= PityInfo.sixStarPity >= type.softGuarantee ? type.softGuarantee : PityInfo.sixStarPity;
if (table.upCharIds.Count > 0)
{
transaction = GetChar(table.upCharIds[0], PityInfo.isFiftyFiftyLost, fifty, sixStars, 6);
if (transaction.itemId != table.upCharIds[0])
{
PityInfo.isFiftyFiftyLost = true;
}
else
{
PityInfo.isFiftyFiftyLost = false;
}
}
else
{
transaction = GetChar("", PityInfo.isFiftyFiftyLost, fifty, sixStars, 6);
}
pityforcalculate = 0;
}
else if (roll < prob5Star || PityInfo.fiveStarPity >= 10)
{
PityInfo.fiveStarPity -= PityInfo.fiveStarPity >= 10 ? 10 : PityInfo.fiveStarPity;
if (table.upCharIds.Count > 1)
{
transaction = GetChar(table.upCharIds[1], false, fifty, fiveStars, 5);
}
else
{
transaction = GetChar("", false, fifty, fiveStars, 5);
}
}
else
{
transaction = GetChar("", false, fifty, fourStars, 4);
}
if(PityInfo.sixStarPity > 65)
{
pityforcalculate++;
}
transactions.Add(transaction);
}
ScGachaSyncPullResult result = new ScGachaSyncPullResult()
{
GachaPoolId = gachaId,
GachaType =table.type,
OriResultIds =
{
},
Star5GotCount = transactions.FindAll(t => t.rarity == 5).Count,
Star6GotCount = transactions.FindAll(t => t.rarity == 6).Count,
FinalResults =
{
},
UpGotCount = transactions.FindAll(t => table.upCharIds.Contains(t.itemId)).Count,
};
foreach (GachaTransaction transaction in transactions)
{
transaction.gachaTemplateId = gachaId;
bool exist = player.chars.Find(c => c.id == transaction.itemId) != null;
result.OriResultIds.Add(transaction.itemId);
result.FinalResults.Add(new ScdGachaFinalResult()
{
IsNew = !exist,
ItemId = !exist ? transaction.itemId : "item_charpotentialup_" + transaction.itemId,
RewardItemId= !exist ? transaction.itemId : "item_charpotentialup_"+ transaction.itemId,
RewardIds =
{
$"reward_{transaction.rarity}starChar_weaponCoin",
},
});
DatabaseManager.db.AddGachaTransaction(transaction);
}
player.Send(ScMsgId.ScGachaSyncPullResult, result,upSeqId);
ScGachaModifyPoolRoleData roleData = new()
{
GachaPoolId = gachaId,
GachaType = table.type,
GachaPoolCategoryRoleData = new()
{
GachaPoolType = table.type,
Star5SoftGuaranteeProgress = PityInfo.fiveStarPity,
SoftGuaranteeProgress = PityInfo.sixStarPity,
TotalPullCount = PityInfo.sixStarPity
},
GachaPoolRoleData = new()
{
GachaPoolId = gachaId,
HardGuaranteeProgress = PityInfo.sixStarPity,
SoftGuaranteeProgress = PityInfo.sixStarPity,
}
};
player.Send(ScMsgId.ScGachaModifyPoolRoleData, roleData, upSeqId);
}
public GachaTransaction GetChar(string upChar,bool guaranteed, double fifty, List<GachaCharPoolItem> items, int rarity)
{
GachaTransaction transaction = new()
{
transactionTime = DateTime.UtcNow.Ticks,
ownerId = player.roleId,
rarity = rarity,
};
if((fifty >= fiftyfifty || guaranteed) && rarity != 4 && upChar.Length >0)
{
transaction.itemId = upChar;
}
else
{
int index = random.Next(0,items.Count);
if (index > items.Count-1)
{
index = items.Count-1;
}
if(index < 0)
{
index = 0;
}
transaction.itemId = items[index].charId;
transaction.hasLost = transaction.itemId != upChar;
}
return transaction;
}
public static GachaHistoryAPI GetGachaHistoryPage(PlayerData data, string banner, int p = 1)
{
GachaHistoryAPI api = new();
int pageSize = 5;
List<GachaTransaction> transactionList = DatabaseManager.db.LoadGachaTransaction(data.roleId, banner);
transactionList = transactionList.OrderByDescending(g => g.transactionTime).ToList();
int maxPages=(int)Math.Ceiling((double)transactionList.Count / pageSize);
api.maxPages = maxPages;
api.curPage = p;
api.transactionList= transactionList.Skip((p - 1) * pageSize).Take(pageSize).ToList();
return api;
}
public class GachaHistoryAPI
{
public int maxPages = 0;
public int curPage = 0;
public List<GachaTransaction> transactionList;
}
}
}