222 lines
7.9 KiB
Zig
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);
|
|
}
|