jingliu-sr/gameserver/src/services/gacha.zig
2025-05-24 00:34:37 +07:00

222 lines
7.9 KiB
Zig

const std = @import("std");
const protocol = @import("protocol");
const Session = @import("../Session.zig");
const Packet = @import("../Packet.zig");
const Data = @import("../data.zig");
const GachaData = @import("../commands/value.zig");
const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;
const CmdID = protocol.CmdID;
// function to check the list if true
fn isInList(id: u32, list: []const u32) bool {
for (list) |item| {
if (item == id) {
return true;
}
}
return false;
}
pub fn onGetGachaInfo(session: *Session, _: *const Packet, allocator: Allocator) !void {
var info = ArrayList(protocol.GachaCeilingAvatar).init(allocator);
for (GachaData.StandardBanner) |id| {
try info.appendSlice(&[_]protocol.GachaCeilingAvatar{
.{ .RepeatedCnt = 300, .avatar_id = id },
});
}
var gacha_info = protocol.GachaInfo.init(allocator);
gacha_info.begin_time = 0;
gacha_info.end_time = 2524608000;
gacha_info.gacha_ceiling = .{
.avatar_list = info,
.is_claimed = false,
.ceiling_num = 200,
};
gacha_info.KMNJNMJFGBG = 1;
gacha_info.GDIFAAHIFBH = 3;
gacha_info.gacha_id = 1001; // standard banner
var rsp = protocol.GetGachaInfoScRsp.init(allocator);
rsp.retcode = 0;
rsp.DJNDMNPEBKA = 20;
rsp.NOPBEBKHIKA = 20;
rsp.NBELNOIPOEK = 900;
rsp.gacha_random = 0;
try rsp.gacha_info_list.append(gacha_info);
try session.send(CmdID.CmdGetGachaInfoScRsp, rsp);
}
pub fn onBuyGoods(session: *Session, packet: *const Packet, allocator: Allocator) !void {
const req = try packet.getProto(protocol.BuyGoodsCsReq, allocator);
var rsp = protocol.BuyGoodsScRsp.init(allocator);
var item = ArrayList(protocol.Item).init(allocator);
try item.appendSlice(&[_]protocol.Item{.{
.ItemId = 101,
.Num = 100,
}});
rsp.retcode = 0;
rsp.GoodsId = req.goods_id;
rsp.GoodsBuyTimes = req.goods_num;
rsp.ShopId = 0;
rsp.ReturnItemList = .{ .ItemList_ = item };
try session.send(CmdID.CmdBuyGoodsScRsp, rsp);
}
pub fn onGetShopList(session: *Session, _: *const Packet, allocator: Allocator) !void {
var rsp = protocol.GetShopListScRsp.init(allocator);
var shop = ArrayList(protocol.Shop).init(allocator);
var goods = ArrayList(protocol.Goods).init(allocator);
try shop.appendSlice(&[_]protocol.Shop{.{
.ShopId = 1000,
.GoodsList = goods,
}});
try goods.appendSlice(&[_]protocol.Goods{.{
.GoodsId = 101001,
.ItemId = 101,
.BuyTimes = 0,
}});
rsp.retcode = 0;
rsp.ShopType = 101;
rsp.ShopList = shop;
try session.send(CmdID.CmdGetShopListScRsp, rsp);
}
pub fn onExchangeHcoin(session: *Session, packet: *const Packet, allocator: Allocator) !void {
const req = try packet.getProto(protocol.ExchangeHcoinCsReq, allocator);
var rsp = protocol.ExchangeHcoinScRsp.init(allocator);
rsp.Num = req.Num;
rsp.retcode = 0;
try session.send(CmdID.CmdExchangeHcoinScRsp, rsp);
}
var five_star_pity: u32 = 0;
var four_star_pity: u32 = 0;
var guaranteed_five_star_rate_up: bool = false;
var guaranteed_four_star_rate_up: bool = false;
var avatar_list_cached: ?std.ArrayList(u32) = null;
var lightcone_list_3_cached: ?std.ArrayList(u32) = null;
var lightcone_list_4_cached: ?std.ArrayList(u32) = null;
fn pow(base: f64, exp: f64) f64 {
return @exp(exp * @log(base));
}
fn getFiveStarRate(gacha_count: u32) f64 {
if (gacha_count < 21) {
return 0.02;
}
if (gacha_count < 72) {
return 0.008;
}
const excess_pulls = @as(f64, @floatFromInt(gacha_count - 71));
return 0.008 + (1.0 - 0.008) * pow(excess_pulls / 18.0, 2.8);
}
fn getFourStarRate(gacha_count: u32) f64 {
if (gacha_count < 6) {
return 0.055;
}
const excess_pulls = @as(f64, @floatFromInt(gacha_count - 5));
return 0.055 + (1.0 - 0.055) * pow(excess_pulls / 3.5, 2.2);
}
fn pickRandomId(rnd: *std.rand.Random, banner: []const u32) u32 {
return banner[rnd.int(usize) % banner.len];
}
pub fn onDoGacha(session: *Session, packet: *const Packet, allocator: std.mem.Allocator) !void {
const req = try packet.getProto(protocol.DoGachaCsReq, allocator);
var rsp = protocol.DoGachaScRsp.init(allocator);
var seed: u64 = undefined;
try std.posix.getrandom(std.mem.asBytes(&seed));
var prng = std.rand.DefaultPrng.init(seed);
var rnd = prng.random();
var selected_ids = std.ArrayList(u32).init(allocator);
defer selected_ids.deinit();
var got_four_star = false;
for (0..req.gacha_num) |_| {
const five_star_rate = getFiveStarRate(five_star_pity);
const four_star_rate = getFourStarRate(four_star_pity);
const random_value = rnd.float(f64);
var selected_banner: []const u32 = &Data.LightconeList_3;
var is_five_star = false;
var is_four_star = false;
if (random_value < five_star_rate or five_star_pity == 89) {
is_five_star = true;
five_star_pity = 0;
if (guaranteed_five_star_rate_up) {
selected_banner = &GachaData.RateUp;
guaranteed_five_star_rate_up = false;
} else {
if (rnd.boolean()) {
selected_banner = &GachaData.RateUp;
} else {
selected_banner = &GachaData.StandardBanner;
guaranteed_five_star_rate_up = true;
}
}
} else if (four_star_pity == 9 or random_value < (five_star_rate + four_star_rate)) {
is_four_star = true;
four_star_pity = 0;
got_four_star = true;
if (guaranteed_four_star_rate_up or rnd.float(f64) < 0.70) {
selected_banner = &GachaData.RateUpFourStars;
guaranteed_four_star_rate_up = false;
} else {
if (rnd.boolean()) {
selected_banner = &Data.AvatarList;
} else {
selected_banner = &Data.LightconeList_4;
}
guaranteed_four_star_rate_up = true;
}
} else {
four_star_pity += 1;
}
five_star_pity += 1;
try selected_ids.append(pickRandomId(&rnd, selected_banner));
}
if (req.gacha_num > 1 and !got_four_star) {
selected_ids.items[rnd.int(usize) % selected_ids.items.len] = pickRandomId(&rnd, &GachaData.RateUpFourStars);
}
for (selected_ids.items) |id| {
var gacha_item = protocol.GachaItem.init(allocator);
gacha_item.gacha_item = .{ .ItemId = id };
gacha_item.is_new = false;
var back_item = std.ArrayList(protocol.Item).init(allocator);
var transfer_item = std.ArrayList(protocol.Item).init(allocator);
if (id < 10000) {
if (isInList(id, &GachaData.RateUp) or isInList(id, &GachaData.StandardBanner)) {
try transfer_item.appendSlice(&[_]protocol.Item{
.{ .ItemId = id + 10000, .Num = 1 },
.{ .ItemId = 252, .Num = 20 },
});
} else {
try transfer_item.append(.{ .ItemId = 252, .Num = 20 });
}
}
try back_item.append(.{ .ItemId = 252, .Num = 20 });
gacha_item.transfer_item_list = .{ .ItemList_ = transfer_item };
gacha_item.token_item = .{ .ItemList_ = back_item };
try rsp.gacha_item_list.append(gacha_item);
}
rsp.gacha_num = req.gacha_num;
rsp.gacha_id = req.gacha_id;
rsp.ceiling_num = 200;
rsp.KMNJNMJFGBG = 1;
rsp.NOPBEBKHIKA = 20;
rsp.GDIFAAHIFBH = 3;
rsp.retcode = 0;
std.debug.print("FIVE STAR PITY: {}, (RATE: {d:.4}%)\n", .{ five_star_pity, getFiveStarRate(five_star_pity) * 100.0 });
std.debug.print("FOUR STAR PITY: {}, (RATE: {d:.4}%)\n", .{ four_star_pity, getFourStarRate(four_star_pity) * 100.0 });
try session.send(protocol.CmdID.CmdDoGachaScRsp, rsp);
}