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); }