diff --git a/config.json b/config.json index ea46987..fba2ccc 100644 --- a/config.json +++ b/config.json @@ -2,15 +2,15 @@ "avatar_config": [ { "name": "Acheron", - "id": 1308, + "id": 1304, "hp": 100, "sp": 0, "level": 80, "promotion": 6, - "rank": 2, + "rank": 6, "lightcone": { - "id": 23024, - "rank": 1, + "id": 23000, + "rank": 2, "level": 80, "promotion": 6 }, @@ -26,15 +26,15 @@ }, { "name": "Hyacine", - "id": 1409, + "id": 1313, "hp": 100, "sp": 50, "level": 80, "promotion": 6, - "rank": 0, + "rank": 6, "lightcone": { - "id": 23042, - "rank": 1, + "id": 23001, + "rank": 2, "level": 80, "promotion": 6 }, @@ -55,10 +55,10 @@ "sp": 50, "level": 80, "promotion": 6, - "rank": 0, + "rank": 6, "lightcone": { - "id": 23043, - "rank": 1, + "id": 23004, + "rank": 2, "level": 80, "promotion": 6 }, @@ -79,10 +79,10 @@ "sp": 0, "level": 80, "promotion": 6, - "rank": 0, + "rank": 6, "lightcone": { - "id": 23038, - "rank": 1, + "id": 23006, + "rank": 2, "level": 80, "promotion": 6 }, @@ -95,6 +95,150 @@ "63196,15,3,4,7:2:1,8:4:7,9:2:1,10:1:1" ], "use_technique": true + }, + { + "name": "Castorice", + "id": 1407, + "hp": 100, + "sp": 50, + "level": 80, + "promotion": 6, + "rank": 6, + "lightcone": { + "id": 23015, + "rank": 2, + "level": 80, + "promotion": 6 + }, + "relics": [ + "61241,15,1,4,8:2:2,6:2:2,4:2:2,2:3:3", + "61242,15,1,4,3:3:3,9:2:2,7:2:2,8:2:2", + "61243,15,5,4,6:2:2,4:2:2,12:1:1,2:4:4", + "61244,15,1,4,5:4:4,8:2:2,1:2:2,6:1:1", + "63195,15,1,4,9:2:2,2:1:1,8:1:1,11:5:5", + "63196,15,3,4,7:1:1,1:3:3,11:4:4,2:1:1" + ], + "use_technique": true + }, + { + "name": "Castorice", + "id": 8008, + "hp": 100, + "sp": 50, + "level": 80, + "promotion": 6, + "rank": 6, + "lightcone": { + "id": 23009, + "rank": 2, + "level": 80, + "promotion": 6 + }, + "relics": [ + "61241,15,1,4,8:2:2,6:2:2,4:2:2,2:3:3", + "61242,15,1,4,3:3:3,9:2:2,7:2:2,8:2:2", + "61243,15,5,4,6:2:2,4:2:2,12:1:1,2:4:4", + "61244,15,1,4,5:4:4,8:2:2,1:2:2,6:1:1", + "63195,15,1,4,9:2:2,2:1:1,8:1:1,11:5:5", + "63196,15,3,4,7:1:1,1:3:3,11:4:4,2:1:1" + ], + "use_technique": true + }, + { + "name": "Castorice", + "id": 1001, + "hp": 100, + "sp": 50, + "level": 80, + "promotion": 6, + "rank": 6, + "lightcone": { + "id": 23012, + "rank": 2, + "level": 80, + "promotion": 6 + }, + "relics": [ + "61241,15,1,4,8:2:2,6:2:2,4:2:2,2:3:3", + "61242,15,1,4,3:3:3,9:2:2,7:2:2,8:2:2", + "61243,15,5,4,6:2:2,4:2:2,12:1:1,2:4:4", + "61244,15,1,4,5:4:4,8:2:2,1:2:2,6:1:1", + "63195,15,1,4,9:2:2,2:1:1,8:1:1,11:5:5", + "63196,15,3,4,7:1:1,1:3:3,11:4:4,2:1:1" + ], + "use_technique": true + }, + { + "name": "The Herta", + "id": 1401, + "hp": 100, + "sp": 50, + "level": 80, + "promotion": 6, + "rank": 6, + "lightcone": { + "id": 23013, + "rank": 2, + "level": 80, + "promotion": 6 + }, + "relics": [ + "61221,15,1,4,12:2:2,3:1:1,10:4:4,5:2:2", + "61222,15,1,4,3:4:4,4:1:1,10:3:3,8:1:1", + "61223,15,5,4,3:3:3,2:3:3,12:1:1,8:2:2", + "61224,15,4,4,6:3:3,2:1:1,12:3:3,8:2:2", + "63145,15,6,4,11:2:2,5:1:1,9:2:2,8:4:4", + "63146,15,4,4,6:3:3,10:1:1,12:4:4,11:1:1" + ], + "use_technique": true + }, + { + "name": "Aglaea", + "id": 1402, + "hp": 100, + "sp": 50, + "level": 80, + "promotion": 6, + "rank": 6, + "lightcone": { + "id": 23033, + "rank": 2, + "level": 80, + "promotion": 6 + }, + "relics": [ + "61221,15,1,4,12:2:2,3:1:1,10:4:4,5:2:2", + "61222,15,1,4,3:4:4,4:1:1,10:3:3,8:1:1", + "61223,15,5,4,3:3:3,2:3:3,12:1:1,8:2:2", + "61224,15,4,4,6:3:3,2:1:1,12:3:3,8:2:2", + "63145,15,6,4,11:2:2,5:1:1,9:2:2,8:4:4", + "63146,15,4,4,6:3:3,10:1:1,12:4:4,11:1:1" + ], + "use_technique": true + }, + { + "name": "Yunli", + "id": 1221, + "hp": 100, + "sp": 50, + "level": 80, + "promotion": 6, + "rank": 6, + "lightcone": { + "id": 23018, + "rank": 2, + "level": 80, + "promotion": 6 + }, + "relics": [ + "61221,15,1,4,12:2:2,3:1:1,10:4:4,5:2:2", + "61222,15,1,4,3:4:4,4:1:1,10:3:3,8:1:1", + "61223,15,5,4,3:3:3,2:3:3,12:1:1,8:2:2", + "61224,15,4,4,6:3:3,2:1:1,12:3:3,8:2:2", + "63145,15,6,4,11:2:2,5:1:1,9:2:2,8:4:4", + "63146,15,4,4,6:3:3,10:1:1,12:4:4,11:1:1" + ], + "use_technique": true } ], "battle_config": { diff --git a/gameserver/src/commands/sync.zig b/gameserver/src/commands/sync.zig index 093973e..fb564fe 100644 --- a/gameserver/src/commands/sync.zig +++ b/gameserver/src/commands/sync.zig @@ -21,13 +21,16 @@ fn isInList(id: u32, list: []const u32) bool { return false; } +pub var max_avatar_list: u32 = 0; + fn syncItems(session: *Session, allocator: Allocator, equip_avatar: bool) !void { + resetGlobalUidGens(); var sync = protocol.PlayerSyncScNotify.init(allocator); const config = try Config.loadGameConfig(allocator, "config.json"); for (config.avatar_config.items) |avatarConf| { const equip_avatar_id: u32 = if (equip_avatar) avatarConf.id else 0; const lc = protocol.Equipment{ - .unique_id = if (equip_avatar) nextGlobalId(.Default) else nextGlobalId(.Undress), + .unique_id = if (equip_avatar) nextGlobalId() else nextGlobalId(), .tid = avatarConf.lightcone.id, .is_protected = true, .level = avatarConf.lightcone.level, @@ -40,7 +43,7 @@ fn syncItems(session: *Session, allocator: Allocator, equip_avatar: bool) !void var r = protocol.Relic{ .tid = input.id, .main_affix_id = input.main_affix_id, - .unique_id = if (equip_avatar) nextGlobalId(.Default) else nextGlobalId(.Undress), + .unique_id = if (equip_avatar) nextGlobalId() else nextGlobalId(), .exp = 0, .equip_avatar_id = equip_avatar_id, .is_protected = true, @@ -58,12 +61,15 @@ fn syncItems(session: *Session, allocator: Allocator, equip_avatar: bool) !void try session.send(CmdID.CmdPlayerSyncScNotify, sync); } -pub fn onSyncEquipment(session: *Session, _: []const u8, allocator: Allocator) Error!void { +pub fn onUndressEquipment(session: *Session, _: []const u8, allocator: Allocator) Error!void { try syncItems(session, allocator, false); +} +pub fn onSyncEquipment(session: *Session, _: []const u8, allocator: Allocator) Error!void { try syncItems(session, allocator, true); } pub fn onSyncAvatar(session: *Session, _: []const u8, allocator: Allocator) Error!void { + resetGlobalUidGens(); var sync = protocol.PlayerSyncScNotify.init(allocator); const config = try Config.loadGameConfig(allocator, "config.json"); var char = protocol.AvatarSync.init(allocator); @@ -103,15 +109,13 @@ pub fn onSyncAvatar(session: *Session, _: []const u8, allocator: Allocator) Erro avatar.level = avatarConf.level; avatar.promotion = avatarConf.promotion; avatar.rank = avatarConf.rank; - avatar.equipment_unique_id = nextGlobalId(.Avatar); - std.debug.print("LIGHTCONE SYNC {}\n", .{avatar.equipment_unique_id}); + avatar.equipment_unique_id = nextGlobalId(); avatar.equip_relic_list = ArrayList(protocol.EquipRelic).init(allocator); for (0..6) |i| { try avatar.equip_relic_list.append(.{ - .relic_unique_id = nextGlobalId(.Avatar), // uid + .relic_unique_id = nextGlobalId(), // uid .type = @intCast(i), // slot }); - std.debug.print("EQUIPING SYNC {}:{}:{}\n", .{ avatarConf.id, avatar.equip_relic_list.items[i].relic_unique_id, i }); } var talentLevel: u32 = 0; const skill_list: []const u32 = if (isInList(avatar.base_avatar_id, &Data.Rem)) &Data.skills else &Data.skills_old; @@ -128,13 +132,13 @@ pub fn onSyncAvatar(session: *Session, _: []const u8, allocator: Allocator) Erro try char.avatar_list.append(avatar); const avatarType: protocol.MultiPathAvatarType = @enumFromInt(avatarConf.id); if (@intFromEnum(avatarType) > 1) { - std.debug.print("setting avatar type: {}\n", .{avatarConf.id}); try session.send(CmdID.CmdSetAvatarPathScRsp, protocol.SetAvatarPathScRsp{ .retcode = 0, .avatar_id = avatarType, }); } } + max_avatar_list = @intCast(config.avatar_config.items.len); sync.avatar_sync = char; try session.send(CmdID.CmdPlayerSyncScNotify, sync); } @@ -142,7 +146,7 @@ pub fn onSyncAvatar(session: *Session, _: []const u8, allocator: Allocator) Erro pub fn onSyncMultiPath(session: *Session, _: []const u8, allocator: Allocator) Error!void { var sync = protocol.PlayerSyncScNotify.init(allocator); const config = try Config.loadGameConfig(allocator, "config.json"); - const currentAvatarId = getCurrentGlobalId(.Avatar); + const currentAvatarId = getCurrentGlobalId(); const GeneratorType = UidGen(); const avatar_ids = [_][]const u32{ &[_]u32{ 8001, 8002 }, @@ -173,7 +177,7 @@ pub fn onSyncMultiPath(session: *Session, _: []const u8, allocator: Allocator) E for (avatar_ids[i]) |id| { if (avatar.id == id) { multis[i].rank = avatar.rank; - indexes[i] = 5 - counts[i]; + indexes[i] = max_avatar_list + 1 - counts[i]; } } } @@ -215,30 +219,25 @@ pub fn onSyncMultiPath(session: *Session, _: []const u8, allocator: Allocator) E try session.send(CmdID.CmdPlayerSyncScNotify, sync); } -pub const UidType = enum { Default, Undress, Avatar }; -pub var global_uid_gen = UidGenerator.init(.Default, 28); -pub var global_uid_gen_undress = UidGenerator.init(.Undress, 0); -pub var global_uid_gen_avatar = UidGenerator.init(.Avatar, 28); -pub fn nextGlobalId(uid_type: UidType) u32 { - return switch (uid_type) { - .Default => global_uid_gen.nextId(), - .Undress => global_uid_gen_undress.nextId(), - .Avatar => global_uid_gen_avatar.nextId(), - }; +pub var global_uid_gen: UidGenerator = undefined; + +fn resetGlobalUidGens() void { + global_uid_gen = UidGenerator.init(0); } -pub fn getCurrentGlobalId(uid_type: UidType) u32 { - return switch (uid_type) { - .Default => global_uid_gen.getCurrentId(), - .Undress => global_uid_gen_undress.getCurrentId(), - .Avatar => global_uid_gen_avatar.getCurrentId(), - }; + +pub fn nextGlobalId() u32 { + return global_uid_gen.nextId(); } + +pub fn getCurrentGlobalId() u32 { + return global_uid_gen.getCurrentId(); +} + pub const UidGenerator = struct { current_id: u32, - uid_type: UidType, - pub fn init(uid_type: UidType, start_id: u32) UidGenerator { - return UidGenerator{ .uid_type = uid_type, .current_id = start_id }; + pub fn init(start_id: u32) UidGenerator { + return UidGenerator{ .current_id = start_id }; } pub fn nextId(self: *UidGenerator) u32 { @@ -250,22 +249,30 @@ pub const UidGenerator = struct { return self.current_id; } }; + pub fn UidGen() type { return struct { current_id: u32, const Self = @This(); - pub fn init(initial_id: u32) Self { - return Self{ .current_id = initial_id }; + + pub fn init(start_id: u32) Self { + return Self{ .current_id = start_id }; } + pub fn nextId(self: *Self) u32 { - self.current_id +%= 1; + self.current_id += 1; + return self.current_id; + } + + pub fn getCurrentId(self: *const Self) u32 { return self.current_id; } }; } pub fn onGenerateAndSync(session: *Session, placeholder: []const u8, allocator: Allocator) Error!void { try commandhandler.sendMessage(session, "Sync items with config\n", allocator); - try onSyncEquipment(session, placeholder, allocator); try onSyncAvatar(session, placeholder, allocator); + try syncItems(session, allocator, false); + try syncItems(session, allocator, true); try onSyncMultiPath(session, placeholder, allocator); } diff --git a/gameserver/src/handlers.zig b/gameserver/src/handlers.zig index 0b53a14..8b5a805 100644 --- a/gameserver/src/handlers.zig +++ b/gameserver/src/handlers.zig @@ -42,6 +42,7 @@ const HandlerList = [_]struct { CmdID, Action }{ .{ CmdID.CmdDressAvatarSkinCsReq, avatar.onDressAvatarSkin }, .{ CmdID.CmdGetBigDataAllRecommendCsReq, avatar.onGetBigDataAll }, .{ CmdID.CmdGetBigDataRecommendCsReq, avatar.onGetBigData }, + .{ CmdID.CmdGetPreAvatarGrowthInfoCsReq, avatar.onGetPreAvatarGrowthInfo }, //bag .{ CmdID.CmdGetBagCsReq, item.onGetBag }, .{ CmdID.CmdUseItemCsReq, item.onUseItem }, @@ -57,6 +58,8 @@ const HandlerList = [_]struct { CmdID, Action }{ .{ CmdID.CmdQuickStartCocoonStageCsReq, battle.onQuickStartCocoonStage }, .{ CmdID.CmdQuickStartFarmElementCsReq, battle.onQuickStartFarmElement }, .{ CmdID.CmdStartBattleCollegeCsReq, battle.onStartBattleCollege }, + .{ CmdID.CmdGetCurBattleInfoCsReq, battle.onGetCurBattleInfo }, + .{ CmdID.CmdSyncClientResVersionCsReq, battle.onSyncClientResVersion }, //gacha .{ CmdID.CmdGetGachaInfoCsReq, gacha.onGetGachaInfo }, .{ CmdID.CmdBuyGoodsCsReq, gacha.onBuyGoods }, @@ -104,6 +107,7 @@ const HandlerList = [_]struct { CmdID, Action }{ .{ CmdID.CmdChangePropTimelineInfoCsReq, scene.onChangePropTimeline }, .{ CmdID.CmdDeactivateFarmElementCsReq, scene.onDeactivateFarmElement }, .{ CmdID.CmdSetGroupCustomSaveDataCsReq, scene.onSetGroupCustomSaveData }, + .{ CmdID.CmdGetEnteredSceneCsReq, scene.onGetEnteredScene }, //events .{ CmdID.CmdGetActivityScheduleConfigCsReq, events.onGetActivity }, .{ CmdID.CmdUpdateServerPrefsDataCsReq, events.onUpdateServerPrefsData }, @@ -112,6 +116,7 @@ const HandlerList = [_]struct { CmdID, Action }{ .{ CmdID.CmdGetChallengeGroupStatisticsCsReq, challenge.onGetChallengeGroupStatistics }, .{ CmdID.CmdStartChallengeCsReq, challenge.onStartChallenge }, .{ CmdID.CmdLeaveChallengeCsReq, challenge.onLeaveChallenge }, + .{ CmdID.CmdGetCurChallengeCsReq, challenge.onGetCurChallengeScRsp }, }; const DummyCmdList = [_]struct { CmdID, CmdID }{ @@ -123,21 +128,18 @@ const DummyCmdList = [_]struct { CmdID, CmdID }{ .{ CmdID.CmdGetAllServerPrefsDataCsReq, CmdID.CmdGetAllServerPrefsDataScRsp }, .{ CmdID.CmdGetMissionDataCsReq, CmdID.CmdGetMissionDataScRsp }, .{ CmdID.CmdGetQuestDataCsReq, CmdID.CmdGetQuestDataScRsp }, - .{ CmdID.CmdGetCurChallengeCsReq, CmdID.CmdGetCurChallengeScRsp }, .{ CmdID.CmdGetRogueCommonDialogueDataCsReq, CmdID.CmdGetRogueCommonDialogueDataScRsp }, .{ CmdID.CmdGetRogueInfoCsReq, CmdID.CmdGetRogueInfoScRsp }, .{ CmdID.CmdGetRogueHandbookDataCsReq, CmdID.CmdGetRogueHandbookDataScRsp }, .{ CmdID.CmdGetRogueEndlessActivityDataCsReq, CmdID.CmdGetRogueEndlessActivityDataScRsp }, .{ CmdID.CmdChessRogueQueryCsReq, CmdID.CmdChessRogueQueryScRsp }, .{ CmdID.CmdRogueTournQueryCsReq, CmdID.CmdRogueTournQueryScRsp }, - .{ CmdID.CmdSyncClientResVersionCsReq, CmdID.CmdSyncClientResVersionScRsp }, .{ CmdID.CmdDailyFirstMeetPamCsReq, CmdID.CmdDailyFirstMeetPamScRsp }, .{ CmdID.CmdGetBattleCollegeDataCsReq, CmdID.CmdGetBattleCollegeDataScRsp }, .{ CmdID.CmdGetNpcStatusCsReq, CmdID.CmdGetNpcStatusScRsp }, .{ CmdID.CmdGetSecretKeyInfoCsReq, CmdID.CmdGetSecretKeyInfoScRsp }, .{ CmdID.CmdGetHeartDialInfoCsReq, CmdID.CmdGetHeartDialInfoScRsp }, .{ CmdID.CmdGetVideoVersionKeyCsReq, CmdID.CmdGetVideoVersionKeyScRsp }, - .{ CmdID.CmdGetCurBattleInfoCsReq, CmdID.CmdGetCurBattleInfoScRsp }, .{ CmdID.CmdHeliobusActivityDataCsReq, CmdID.CmdHeliobusActivityDataScRsp }, .{ CmdID.CmdGetAetherDivideInfoCsReq, CmdID.CmdGetAetherDivideInfoScRsp }, .{ CmdID.CmdGetMapRotationDataCsReq, CmdID.CmdGetMapRotationDataScRsp }, @@ -162,7 +164,6 @@ const DummyCmdList = [_]struct { CmdID, CmdID }{ .{ CmdID.CmdRogueArcadeGetInfoCsReq, CmdID.CmdRogueArcadeGetInfoScRsp }, .{ CmdID.CmdGetMissionMessageInfoCsReq, CmdID.CmdGetMissionMessageInfoScRsp }, .{ CmdID.CmdTrainPartyGetDataCsReq, CmdID.CmdTrainPartyGetDataScRsp }, - .{ CmdID.CmdGetEnteredSceneCsReq, CmdID.CmdGetEnteredSceneScRsp }, .{ CmdID.CmdQueryProductInfoCsReq, CmdID.CmdQueryProductInfoScRsp }, .{ CmdID.CmdGetPamSkinDataCsReq, CmdID.CmdGetPamSkinDataScRsp }, .{ CmdID.CmdGetRogueScoreRewardInfoCsReq, CmdID.CmdGetRogueScoreRewardInfoScRsp }, diff --git a/gameserver/src/manager/battle_mgr.zig b/gameserver/src/manager/battle_mgr.zig index 8ebc07b..208e6df 100644 --- a/gameserver/src/manager/battle_mgr.zig +++ b/gameserver/src/manager/battle_mgr.zig @@ -10,7 +10,9 @@ const ArrayList = std.ArrayList; const Allocator = std.mem.Allocator; const CmdID = protocol.CmdID; -// function to check the list if true +pub var selectedAvatarID = [_]u32{ 1304, 1313, 1406, 1004 }; + +// Function to check if an ID is in a list fn isInList(id: u32, list: []const u32) bool { for (list) |item| { if (item == id) { @@ -19,446 +21,7 @@ fn isInList(id: u32, list: []const u32) bool { } return false; } -pub const BattleManager = struct { - allocator: std.mem.Allocator, - pub fn init(allocator: std.mem.Allocator) BattleManager { - return BattleManager{ .allocator = allocator }; - } - pub fn createBattle(self: *BattleManager) !protocol.SceneBattleInfo { - const config = try Config.loadGameConfig(self.allocator, "config.json"); - - var battle = protocol.SceneBattleInfo.init(self.allocator); - const BattleBuff = protocol.BattleBuff; - - // Avatar handler - for (config.avatar_config.items, 0..) |avatarConf, idx| { - var avatar = protocol.BattleAvatar.init(self.allocator); - avatar.id = avatarConf.id; - avatar.hp = avatarConf.hp * 100; - avatar.sp_bar = .{ .sp_cur = avatarConf.sp * 100, .sp_max = 10000 }; - avatar.level = avatarConf.level; - avatar.rank = avatarConf.rank; - avatar.promotion = avatarConf.promotion; - avatar.avatar_type = .AVATAR_FORMAL_TYPE; - - // Relics - for (avatarConf.relics.items) |relic| { - const r = try relicCoder(self.allocator, relic.id, relic.level, relic.main_affix_id, relic.stat1, relic.cnt1, relic.step1, relic.stat2, relic.cnt2, relic.step2, relic.stat3, relic.cnt3, relic.step3, relic.stat4, relic.cnt4, relic.step4); - try avatar.relic_list.append(r); - } - - // Lightcone (LC) - const lc = protocol.BattleEquipment{ - .id = avatarConf.lightcone.id, - .rank = avatarConf.lightcone.rank, - .level = avatarConf.lightcone.level, - .promotion = avatarConf.lightcone.promotion, - }; - try avatar.equipment_list.append(lc); - //max trace - var talentLevel: u32 = 0; - const skill_list: []const u32 = if (isInList(avatar.id, &Data.Rem)) &Data.skills else &Data.skills_old; - for (skill_list) |elem| { - talentLevel = switch (elem) { - 1 => 6, - 2...4 => 10, - 301, 302 => if (isInList(avatar.id, &Data.Rem)) 6 else 1, - else => 1, - }; - const talent = protocol.AvatarSkillTree{ .point_id = avatar.id * 1000 + elem, .level = talentLevel }; - try avatar.skilltree_list.append(talent); - } - // enable technique - if (avatarConf.use_technique) { - var targetIndexList = ArrayList(u32).init(self.allocator); - try targetIndexList.append(0); - //Add new ID when modifying for new patch - - var buffedAvatarId = avatar.id; - if (avatar.id == 8004) { - buffedAvatarId = 8003; - } else if (avatar.id == 8006) { - buffedAvatarId = 8005; - } else if (avatar.id == 8008) { - buffedAvatarId = 8007; - } - - for (Data.buffs_unlocked) |buffId| { - const idPrefix = buffId / 100; - if (idPrefix == buffedAvatarId) { - var buff = BattleBuff{ - .id = buffId, - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - - try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff); - } - } - - if (isInList(buffedAvatarId, &Data.IgnoreToughness)) { - var buff_tough = BattleBuff{ - .id = 1000119, //for is_ignore toughness - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try buff_tough.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff_tough); - } - - if (buffedAvatarId == 1224) { - var buff_march = protocol.BattleBuff{ - .id = 122401, //for hunt march 7th tech - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - - try buff_march.dynamic_values.appendSlice(&[_]protocol.BattleBuff.DynamicValuesEntry{ - .{ .key = .{ .Const = "#ADF_1" }, .value = 3 }, - .{ .key = .{ .Const = "#ADF_2" }, .value = 3 }, - }); - try battle.buff_list.append(buff_march); - } - - if (buffedAvatarId == 1310) { - var buff_firefly = BattleBuff{ - .id = 1000112, //for firefly tech - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try buff_firefly.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff_firefly); - } - - if (buffedAvatarId == 8007) { - var buff_rmc = BattleBuff{ - .id = 800701, //for rmc tech - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try buff_rmc.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff_rmc); - } - } - - try battle.pve_avatar_list.append(avatar); - } - - // Basic battle info - battle.battle_id = config.battle_config.battle_id; - battle.stage_id = config.battle_config.stage_id; - battle.logic_random_seed = @intCast(@mod(std.time.timestamp(), 0xFFFFFFFF)); - battle.rounds_limit = config.battle_config.cycle_count; - battle.monster_wave_length = @intCast(config.battle_config.monster_wave.items.len); - battle.world_level = 6; - - // Monster handler - for (config.battle_config.monster_wave.items) |wave| { - var monster_wave = protocol.SceneMonsterWave.init(self.allocator); - monster_wave.wave_param = protocol.SceneMonsterWaveParam{ .level = config.battle_config.monster_level }; - for (wave.items) |mob_id| { - try monster_wave.monster_list.append(.{ .monster_id = mob_id }); - } - try battle.monster_wave_list.append(monster_wave); - } - // stage blessings - for (config.battle_config.blessings.items) |blessing| { - var targetIndexList = ArrayList(u32).init(self.allocator); - try targetIndexList.append(0); - var buff = protocol.BattleBuff{ - .id = blessing, - .level = 1, - .owner_index = 0xffffffff, - .wave_flag = 0xffffffff, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff); - } - if (isInList(1407, Data.AllAvatars)) { //support Castorice - var targetIndexList = ArrayList(u32).init(self.allocator); - try targetIndexList.append(0); - var mazebuff_data = BattleBuff{ - .id = 140703, - .level = 1, - .owner_index = 1, - .wave_flag = @intCast(config.battle_config.monster_wave.items.len), - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try mazebuff_data.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(mazebuff_data); - } - - // PF/AS scoring - const BattleTargetInfoEntry = protocol.SceneBattleInfo.BattleTargetInfoEntry; - battle.battle_target_info = ArrayList(BattleTargetInfoEntry).init(self.allocator); - - // target hardcode - var pfTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) }; - try pfTargetHead.battle_target_list.append(.{ .id = 10002, .progress = 0, .total_progress = 0 }); - var pfTargetTail = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) }; - try pfTargetTail.battle_target_list.append(.{ .id = 2001, .progress = 0, .total_progress = 0 }); - try pfTargetTail.battle_target_list.append(.{ .id = 2002, .progress = 0, .total_progress = 0 }); - var asTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) }; - try asTargetHead.battle_target_list.append(.{ .id = 90005, .progress = 0, .total_progress = 0 }); - - switch (battle.stage_id) { - // PF - 30019000...30019100, 30021000...30021100, 30301000...30319000 => { - try battle.battle_target_info.append(.{ .key = 1, .value = pfTargetHead }); - // fill blank target - for (2..5) |i| { - try battle.battle_target_info.append(.{ .key = @intCast(i) }); - } - try battle.battle_target_info.append(.{ .key = 5, .value = pfTargetTail }); - }, - // AS - 420100...420300 => { - try battle.battle_target_info.append(.{ .key = 1, .value = asTargetHead }); - }, - else => {}, - } - return battle; - } -}; -pub const ChallegeStageManager = struct { - allocator: std.mem.Allocator, - pub fn init(allocator: std.mem.Allocator) ChallegeStageManager { - return ChallegeStageManager{ .allocator = allocator }; - } - pub fn createChallegeStage( - self: *ChallegeStageManager, - ) !protocol.SceneBattleInfo { - const config = try Config.loadGameConfig(self.allocator, "config.json"); - const stage = try Config.loadStageConfig(self.allocator, "resources/StageConfig.json"); - var battle = protocol.SceneBattleInfo.init(self.allocator); - const BattleBuff = protocol.BattleBuff; - for (stage.stage_config.items) |stageConf| { - if (stageConf.stage_id == ChallengeData.challenge_stageID) { - for (config.avatar_config.items, 0..) |avatarConf, idx| { - var avatar = protocol.BattleAvatar.init(self.allocator); - avatar.id = avatarConf.id; - avatar.hp = avatarConf.hp * 100; - avatar.sp_bar = .{ .sp_cur = avatarConf.sp * 100, .sp_max = 10000 }; - avatar.level = avatarConf.level; - avatar.rank = avatarConf.rank; - avatar.promotion = avatarConf.promotion; - avatar.avatar_type = .AVATAR_FORMAL_TYPE; - - for (avatarConf.relics.items) |relic| { - const r = try relicCoder(self.allocator, relic.id, relic.level, relic.main_affix_id, relic.stat1, relic.cnt1, relic.step1, relic.stat2, relic.cnt2, relic.step2, relic.stat3, relic.cnt3, relic.step3, relic.stat4, relic.cnt4, relic.step4); - try avatar.relic_list.append(r); - } - - const lc = protocol.BattleEquipment{ - .id = avatarConf.lightcone.id, - .rank = avatarConf.lightcone.rank, - .level = avatarConf.lightcone.level, - .promotion = avatarConf.lightcone.promotion, - }; - try avatar.equipment_list.append(lc); - var talentLevel: u32 = 0; - const skill_list: []const u32 = if (isInList(avatar.id, &Data.Rem)) &Data.skills else &Data.skills_old; - for (skill_list) |elem| { - talentLevel = switch (elem) { - 1 => 6, - 2...4 => 10, - 301, 302 => if (isInList(avatar.id, &Data.Rem)) 6 else 1, - else => 1, - }; - const talent = protocol.AvatarSkillTree{ .point_id = avatar.id * 1000 + elem, .level = talentLevel }; - try avatar.skilltree_list.append(talent); - } - if (avatarConf.use_technique) { - var targetIndexList = ArrayList(u32).init(self.allocator); - try targetIndexList.append(0); - - var buffedAvatarId = avatar.id; - if (avatar.id == 8004) { - buffedAvatarId = 8003; - } else if (avatar.id == 8006) { - buffedAvatarId = 8005; - } else if (avatar.id == 8008) { - buffedAvatarId = 8007; - } - - for (Data.buffs_unlocked) |buffId| { - const idPrefix = buffId / 100; - if (idPrefix == buffedAvatarId) { - var buff = BattleBuff{ - .id = buffId, - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - - try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff); - } - } - - if (isInList(buffedAvatarId, &Data.IgnoreToughness)) { - var buff_tough = BattleBuff{ - .id = 1000119, //for is_ignore toughness - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try buff_tough.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff_tough); - } - - if (buffedAvatarId == 1224) { - var buff_march = protocol.BattleBuff{ - .id = 122401, //for hunt march 7th tech - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - - try buff_march.dynamic_values.appendSlice(&[_]protocol.BattleBuff.DynamicValuesEntry{ - .{ .key = .{ .Const = "#ADF_1" }, .value = 3 }, - .{ .key = .{ .Const = "#ADF_2" }, .value = 3 }, - }); - try battle.buff_list.append(buff_march); - } - - if (buffedAvatarId == 1310) { - var buff_firefly = BattleBuff{ - .id = 1000112, //for firefly tech - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try buff_firefly.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff_firefly); - } - - if (buffedAvatarId == 8007) { - var buff_rmc = BattleBuff{ - .id = 800701, //for rmc tech - .level = 1, - .owner_index = @intCast(idx), - .wave_flag = 1, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try buff_rmc.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff_rmc); - } - } - - try battle.pve_avatar_list.append(avatar); - } - - battle.battle_id = 1; - battle.stage_id = stageConf.stage_id; - battle.logic_random_seed = @intCast(@mod(std.time.timestamp(), 0xFFFFFFFF)); - battle.rounds_limit = if (ChallengeData.challenge_mode != 1) 30 else 4; - battle.monster_wave_length = @intCast(stageConf.monster_list.items.len); - battle.world_level = 6; - - // Monster handler - for (stageConf.monster_list.items) |wave| { - var monster_wave = protocol.SceneMonsterWave.init(self.allocator); - monster_wave.wave_param = protocol.SceneMonsterWaveParam{ .level = stageConf.level }; - for (wave.items) |mob_id| { - try monster_wave.monster_list.append(.{ .monster_id = mob_id }); - } - try battle.monster_wave_list.append(monster_wave); - } - // stage blessings - for (ChallengeData.challenge_blessing) |blessing| { - var targetIndexList = ArrayList(u32).init(self.allocator); - try targetIndexList.append(0); - var buff = protocol.BattleBuff{ - .id = blessing, - .level = 1, - .owner_index = 0xffffffff, - .wave_flag = 0xffffffff, - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(buff); - } - if (isInList(1407, Data.AllAvatars)) { //support Castorice - var targetIndexList = ArrayList(u32).init(self.allocator); - try targetIndexList.append(0); - var mazebuff_data = BattleBuff{ - .id = 140703, - .level = 1, - .owner_index = 1, - .wave_flag = @intCast(stageConf.monster_list.items.len), - .target_index_list = targetIndexList, - .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator), - }; - try mazebuff_data.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); - try battle.buff_list.append(mazebuff_data); - } - - // PF/AS scoring - const BattleTargetInfoEntry = protocol.SceneBattleInfo.BattleTargetInfoEntry; - battle.battle_target_info = ArrayList(BattleTargetInfoEntry).init(self.allocator); - - // target hardcode - var pfTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) }; - try pfTargetHead.battle_target_list.append(.{ .id = 10002, .progress = 0, .total_progress = 0 }); - var pfTargetTail = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) }; - try pfTargetTail.battle_target_list.append(.{ .id = 2001, .progress = 0, .total_progress = 0 }); - try pfTargetTail.battle_target_list.append(.{ .id = 2002, .progress = 0, .total_progress = 0 }); - var asTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) }; - try asTargetHead.battle_target_list.append(.{ .id = 90005, .progress = 0, .total_progress = 0 }); - - switch (battle.stage_id) { - // PF - 30019000...30019100, 30021000...30021100, 30301000...30319000 => { - try battle.battle_target_info.append(.{ .key = 1, .value = pfTargetHead }); - // fill blank target - for (2..5) |i| { - try battle.battle_target_info.append(.{ .key = @intCast(i) }); - } - try battle.battle_target_info.append(.{ .key = 5, .value = pfTargetTail }); - }, - // AS - 420100...420300 => { - try battle.battle_target_info.append(.{ .key = 1, .value = asTargetHead }); - }, - else => {}, - } - } - } - return battle; - } -}; - -pub fn relicCoder(allocator: Allocator, id: u32, level: u32, main_affix_id: u32, stat1: u32, cnt1: u32, step1: u32, stat2: u32, cnt2: u32, step2: u32, stat3: u32, cnt3: u32, step3: u32, stat4: u32, cnt4: u32, step4: u32) !protocol.BattleRelic { +fn createBattleRelic(allocator: Allocator, id: u32, level: u32, main_affix_id: u32, stat1: u32, cnt1: u32, step1: u32, stat2: u32, cnt2: u32, step2: u32, stat3: u32, cnt3: u32, step3: u32, stat4: u32, cnt4: u32, step4: u32) !protocol.BattleRelic { var relic = protocol.BattleRelic{ .id = id, .main_affix_id = main_affix_id, @@ -469,6 +32,305 @@ pub fn relicCoder(allocator: Allocator, id: u32, level: u32, main_affix_id: u32, try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat2, .cnt = cnt2, .step = step2 }); try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat3, .cnt = cnt3, .step = step3 }); try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat4, .cnt = cnt4, .step = step4 }); - return relic; } +fn createBattleAvatar(allocator: Allocator, avatarConf: Config.Avatar) !protocol.BattleAvatar { + var avatar = protocol.BattleAvatar.init(allocator); + avatar.id = avatarConf.id; + avatar.hp = avatarConf.hp * 100; + avatar.sp_bar = .{ .sp_cur = avatarConf.sp * 100, .sp_max = 10000 }; + avatar.level = avatarConf.level; + avatar.rank = avatarConf.rank; + avatar.promotion = avatarConf.promotion; + avatar.avatar_type = .AVATAR_FORMAL_TYPE; + + // Relics + for (avatarConf.relics.items) |relic| { + const r = try createBattleRelic(allocator, relic.id, relic.level, relic.main_affix_id, relic.stat1, relic.cnt1, relic.step1, relic.stat2, relic.cnt2, relic.step2, relic.stat3, relic.cnt3, relic.step3, relic.stat4, relic.cnt4, relic.step4); + try avatar.relic_list.append(r); + } + + // Lightcone + const lc = protocol.BattleEquipment{ + .id = avatarConf.lightcone.id, + .rank = avatarConf.lightcone.rank, + .level = avatarConf.lightcone.level, + .promotion = avatarConf.lightcone.promotion, + }; + try avatar.equipment_list.append(lc); + + // Skills + var talentLevel: u32 = 0; + const skill_list: []const u32 = if (isInList(avatar.id, &Data.Rem)) &Data.skills else &Data.skills_old; + for (skill_list) |elem| { + talentLevel = switch (elem) { + 1 => 6, + 2...4 => 10, + 301, 302 => if (isInList(avatar.id, &Data.Rem)) 6 else 1, + else => 1, + }; + const talent = protocol.AvatarSkillTree{ .point_id = avatar.id * 1000 + elem, .level = talentLevel }; + try avatar.skilltree_list.append(talent); + } + return avatar; +} + +// Function to add technique buffs +fn addTechniqueBuffs(allocator: Allocator, battle: *protocol.SceneBattleInfo, avatar: protocol.BattleAvatar, avatarConf: Config.Avatar, avatar_index: u32) !void { + if (!avatarConf.use_technique) return; + + var targetIndexList = ArrayList(u32).init(allocator); + try targetIndexList.append(0); + + var buffedAvatarId = avatar.id; + if (avatar.id == 8004) { + buffedAvatarId = 8003; + } else if (avatar.id == 8006) { + buffedAvatarId = 8005; + } else if (avatar.id == 8008) { + buffedAvatarId = 8007; + } + + for (Data.buffs_unlocked) |buffId| { + const idPrefix = buffId / 100; + if (idPrefix == buffedAvatarId) { + var buff = protocol.BattleBuff{ + .id = buffId, + .level = 1, + .owner_index = @intCast(avatar_index), + .wave_flag = 1, + .target_index_list = targetIndexList, + .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator), + }; + try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); + try battle.buff_list.append(buff); + } + } + + if (isInList(buffedAvatarId, &Data.IgnoreToughness)) { + var buff_tough = protocol.BattleBuff{ + .id = 1000119, //for is_ignore toughness + .level = 1, + .owner_index = @intCast(avatar_index), + .wave_flag = 1, + .target_index_list = targetIndexList, + .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator), + }; + try buff_tough.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); + try battle.buff_list.append(buff_tough); + } + + if (buffedAvatarId == 1224) { + var buff_march = protocol.BattleBuff{ + .id = 122401, //for hunt march 7th tech + .level = 1, + .owner_index = @intCast(avatar_index), + .wave_flag = 1, + .target_index_list = targetIndexList, + .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator), + }; + try buff_march.dynamic_values.appendSlice(&[_]protocol.BattleBuff.DynamicValuesEntry{ + .{ .key = .{ .Const = "#ADF_1" }, .value = 3 }, + .{ .key = .{ .Const = "#ADF_2" }, .value = 3 }, + }); + try battle.buff_list.append(buff_march); + } + + if (buffedAvatarId == 1310) { + var buff_firefly = protocol.BattleBuff{ + .id = 1000112, //for firefly tech + .level = 1, + .owner_index = @intCast(avatar_index), + .wave_flag = 1, + .target_index_list = targetIndexList, + .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator), + }; + try buff_firefly.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); + try battle.buff_list.append(buff_firefly); + } + + if (buffedAvatarId == 8007) { + var buff_rmc = protocol.BattleBuff{ + .id = 800701, //for rmc tech + .level = 1, + .owner_index = @intCast(avatar_index), + .wave_flag = 1, + .target_index_list = targetIndexList, + .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator), + }; + try buff_rmc.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); + try battle.buff_list.append(buff_rmc); + } +} + +// Function to add future global buff. +fn addGolbalPassive(allocator: Allocator, battle: *protocol.SceneBattleInfo, wave_flag: u32) !void { + if (isInList(1407, Data.AllAvatars)) { //support Castorice + var targetIndexList = ArrayList(u32).init(allocator); + try targetIndexList.append(0); + var mazebuff_data = protocol.BattleBuff{ + .id = 140703, + .level = 1, + .owner_index = 1, + .wave_flag = wave_flag, + .target_index_list = targetIndexList, + .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator), + }; + try mazebuff_data.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); + try battle.buff_list.append(mazebuff_data); + } +} +fn createBattleInfo(allocator: Allocator, config: Config.GameConfig, stage_monster_wave_len: u32, stage_id: u32, rounds_limit: u32) protocol.SceneBattleInfo { + var battle = protocol.SceneBattleInfo.init(allocator); + battle.battle_id = config.battle_config.battle_id; + battle.stage_id = stage_id; + battle.logic_random_seed = @intCast(@mod(std.time.timestamp(), 0xFFFFFFFF)); + battle.rounds_limit = rounds_limit; + battle.monster_wave_length = @intCast(stage_monster_wave_len); + battle.world_level = 6; + return battle; +} +fn addMonsterWaves(allocator: Allocator, battle: *protocol.SceneBattleInfo, monster_wave_configs: std.ArrayList(std.ArrayList(u32)), monster_level: u32) !void { // Added monster_level + for (monster_wave_configs.items) |wave| { + var monster_wave = protocol.SceneMonsterWave.init(allocator); + monster_wave.wave_param = protocol.SceneMonsterWaveParam{ .level = monster_level }; + for (wave.items) |mob_id| { + try monster_wave.monster_list.append(.{ .monster_id = mob_id }); + } + try battle.monster_wave_list.append(monster_wave); + } +} +fn addStageBlessings(allocator: Allocator, battle: *protocol.SceneBattleInfo, blessings: []const u32) !void { + for (blessings) |blessing| { + var targetIndexList = ArrayList(u32).init(allocator); + try targetIndexList.append(0); + var buff = protocol.BattleBuff{ + .id = blessing, + .level = 1, + .owner_index = 0xffffffff, + .wave_flag = 0xffffffff, + .target_index_list = targetIndexList, + .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator), + }; + try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 }); + try battle.buff_list.append(buff); + } +} +fn addBattleTargets(allocator: Allocator, battle: *protocol.SceneBattleInfo) !void { + // PF/AS scoring + battle.battle_target_info = ArrayList(protocol.SceneBattleInfo.BattleTargetInfoEntry).init(allocator); + + // target hardcode + var pfTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) }; + try pfTargetHead.battle_target_list.append(.{ .id = 10002, .progress = 0, .total_progress = 0 }); + var pfTargetTail = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) }; + try pfTargetTail.battle_target_list.append(.{ .id = 2001, .progress = 0, .total_progress = 0 }); + try pfTargetTail.battle_target_list.append(.{ .id = 2002, .progress = 0, .total_progress = 0 }); + var asTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) }; + try asTargetHead.battle_target_list.append(.{ .id = 90005, .progress = 0, .total_progress = 0 }); + + switch (battle.stage_id) { + // PF + 30019000...30019100, 30021000...30021100, 30301000...30319000 => { + try battle.battle_target_info.append(.{ .key = 1, .value = pfTargetHead }); + // fill blank target + for (2..5) |i| { + try battle.battle_target_info.append(.{ .key = @intCast(i) }); + } + try battle.battle_target_info.append(.{ .key = 5, .value = pfTargetTail }); + }, + // AS + 420100...420300 => { + try battle.battle_target_info.append(.{ .key = 1, .value = asTargetHead }); + }, + else => {}, + } +} +pub const BattleManager = struct { + allocator: Allocator, + + pub fn init(allocator: Allocator) BattleManager { + return BattleManager{ .allocator = allocator }; + } + + pub fn createBattle(self: *BattleManager) !protocol.SceneBattleInfo { + const config = try Config.loadGameConfig(self.allocator, "config.json"); + var battle = createBattleInfo(self.allocator, config, @intCast(config.battle_config.monster_wave.items.len), config.battle_config.stage_id, config.battle_config.cycle_count); + var avatarIndex: u32 = 0; + var initial_mode = false; + while (true) { + for (config.avatar_config.items) |avatarConf| { + if (!initial_mode) { + if (!isInList(avatarConf.id, &selectedAvatarID)) { + continue; + } + } + const avatar = try createBattleAvatar(self.allocator, avatarConf); + try addTechniqueBuffs(self.allocator, &battle, avatar, avatarConf, avatarIndex); + try battle.pve_avatar_list.append(avatar); + avatarIndex += 1; + if (avatarIndex >= 4) { + break; + } + } + if (avatarIndex == 0 and !initial_mode) { + initial_mode = true; + continue; + } + break; + } + try addMonsterWaves(self.allocator, &battle, config.battle_config.monster_wave, config.battle_config.monster_level); + try addStageBlessings(self.allocator, &battle, config.battle_config.blessings.items); + try addGolbalPassive(self.allocator, &battle, @intCast(config.battle_config.monster_wave.items.len)); + try addBattleTargets(self.allocator, &battle); + return battle; + } +}; + +pub const ChallegeStageManager = struct { + allocator: Allocator, + + pub fn init(allocator: Allocator) ChallegeStageManager { + return ChallegeStageManager{ .allocator = allocator }; + } + + pub fn createChallegeStage(self: *ChallegeStageManager) !protocol.SceneBattleInfo { + const config = try Config.loadGameConfig(self.allocator, "config.json"); + const stage = try Config.loadStageConfig(self.allocator, "resources/StageConfig.json"); + var battle: protocol.SceneBattleInfo = undefined; + for (stage.stage_config.items) |stageConf| { + if (stageConf.stage_id == ChallengeData.challenge_stageID) { + battle = createBattleInfo(self.allocator, config, @intCast(stageConf.monster_list.items.len), stageConf.stage_id, if (ChallengeData.challenge_mode != 1) 30 else 4); + var avatarIndex: u32 = 0; + var initial_mode = false; + while (true) { + for (config.avatar_config.items) |avatarConf| { + if (!initial_mode) { + if (!isInList(avatarConf.id, &selectedAvatarID)) { + continue; + } + } + const avatar = try createBattleAvatar(self.allocator, avatarConf); + try addTechniqueBuffs(self.allocator, &battle, avatar, avatarConf, avatarIndex); + try battle.pve_avatar_list.append(avatar); + avatarIndex += 1; + if (avatarIndex >= 4) { + break; + } + } + if (avatarIndex == 0 and !initial_mode) { + initial_mode = true; + continue; + } + break; + } + try addMonsterWaves(self.allocator, &battle, stageConf.monster_list, stageConf.level); + try addStageBlessings(self.allocator, &battle, ChallengeData.challenge_blessing); + try addGolbalPassive(self.allocator, &battle, @intCast(stageConf.monster_list.items.len)); + try addBattleTargets(self.allocator, &battle); + break; + } + } + return battle; + } +}; diff --git a/gameserver/src/manager/challenge_mgr.zig b/gameserver/src/manager/challenge_mgr.zig new file mode 100644 index 0000000..41f3e8a --- /dev/null +++ b/gameserver/src/manager/challenge_mgr.zig @@ -0,0 +1,158 @@ +const std = @import("std"); +const protocol = @import("protocol"); +const Session = @import("../Session.zig"); +const Packet = @import("../Packet.zig"); +const Config = @import("../services/config.zig"); +const Data = @import("../data.zig"); +const ChallengeData = @import("../services/challenge.zig"); +const NodeCheck = @import("../commands/value.zig"); + +const ArrayList = std.ArrayList; +const Allocator = std.mem.Allocator; +const CmdID = protocol.CmdID; + +fn contains(list: *const std.ArrayListAligned(u32, null), value: u32) bool { + for (list.items) |item| { + if (item == value) { + return true; + } + } + return false; +} + +pub const ChallengeManager = struct { + allocator: std.mem.Allocator, + pub fn init(allocator: std.mem.Allocator) ChallengeManager { + return ChallengeManager{ .allocator = allocator }; + } + pub fn createChallenge( + self: *ChallengeManager, + challenge_id: u32, + buff_id: u32, + ) !protocol.CurChallenge { + const challenge_config = try Config.loadChallengeConfig(self.allocator, "resources/ChallengeMazeConfig.json"); + const entrance_config = try Config.loadMapEntranceConfig(self.allocator, "resources/MapEntrance.json"); + const maze_config = try Config.loadMazePlaneConfig(self.allocator, "resources/MazePlane.json"); + + var challenge_blessing_list = ChallengeData.ChallengeBlessing{ + .allocator = std.heap.page_allocator, + .items = &.{}, + .capacity = 0, + }; + + var cur_challenge_info = protocol.CurChallenge.init(self.allocator); + cur_challenge_info.challenge_id = challenge_id; + cur_challenge_info.score_id = if (challenge_id > 20000 and challenge_id < 30000) 40000 else 0; + cur_challenge_info.score_two = 0; + cur_challenge_info.status = protocol.ChallengeStatus.CHALLENGE_DOING; + cur_challenge_info.extra_lineup_type = if (NodeCheck.challenge_node == 0) protocol.ExtraLineupType.LINEUP_CHALLENGE else protocol.ExtraLineupType.LINEUP_CHALLENGE_2; + if (NodeCheck.challenge_node == 0) { + for (challenge_config.challenge_config.items) |challengeConf| { + if (challengeConf.id == challenge_id) { + std.debug.print("TRACING CONFIG ID {} WITH CHALLENGE ID {}\n", .{ challengeConf.id, challenge_id }); + for (entrance_config.map_entrance_config.items) |entrance| { + if (entrance.id == challengeConf.map_entrance_id) { + for (maze_config.maze_plane_config.items) |maze| { + if (contains(&maze.floor_id_list, entrance.floor_id)) { + if (challenge_id > 20000 and challenge_id < 30000) { + var story_buff = protocol.ChallengeStoryBuffList{ + .buff_list = ArrayList(u32).init(self.allocator), + }; + try story_buff.buff_list.append(challengeConf.maze_buff_id); + try story_buff.buff_list.append(buff_id); + try challenge_blessing_list.appendSlice(story_buff.buff_list.items); + cur_challenge_info.stage_info = .{ + .IEICDGGELNE = .{ + .cur_story_buffs = story_buff, + }, + }; + ChallengeData.challenge_mode = 1; + } else if (challenge_id > 30000) { + var boss_buff = protocol.ChallengeBossBuffList{ + .buff_list = ArrayList(u32).init(self.allocator), + .challenge_boss_const = 1, + }; + try boss_buff.buff_list.append(challengeConf.maze_buff_id); + try boss_buff.buff_list.append(buff_id); + try challenge_blessing_list.appendSlice(boss_buff.buff_list.items); + cur_challenge_info.stage_info = .{ + .IEICDGGELNE = .{ + .cur_boss_buffs = boss_buff, + }, + }; + ChallengeData.challenge_mode = 2; + } + ChallengeData.challenge_floorID = entrance.floor_id; + ChallengeData.challenge_worldID = maze.world_id; + ChallengeData.challenge_monsterID = challengeConf.npc_monster_id_list1.items[challengeConf.npc_monster_id_list1.items.len - 1]; + ChallengeData.challenge_eventID = challengeConf.event_id_list1.items[challengeConf.event_id_list1.items.len - 1]; + ChallengeData.challenge_groupID = challengeConf.maze_group_id1; + ChallengeData.challenge_maze_groupID = challengeConf.maze_group_id1; + ChallengeData.challenge_planeID = maze.challenge_plane_id; + ChallengeData.challenge_entryID = challengeConf.map_entrance_id; + } + } + } + } + } + } + } else { + for (challenge_config.challenge_config.items) |challengeConf| { + if (challengeConf.id == challenge_id) { + std.debug.print("TRACING CONFIG ID {} WITH CHALLENGE ID {}\n", .{ challengeConf.id, challenge_id }); + for (entrance_config.map_entrance_config.items) |entrance| { + if (entrance.id == challengeConf.map_entrance_id2) { + for (maze_config.maze_plane_config.items) |maze| { + if (contains(&maze.floor_id_list, entrance.floor_id)) { + if (challengeConf.maze_group_id2) |id| { + if (challenge_id > 20000 and challenge_id < 30000) { + var story_buff = protocol.ChallengeStoryBuffList{ + .buff_list = ArrayList(u32).init(self.allocator), + }; + try story_buff.buff_list.append(challengeConf.maze_buff_id); + try story_buff.buff_list.append(buff_id); + try challenge_blessing_list.appendSlice(story_buff.buff_list.items); + cur_challenge_info.stage_info = .{ + .IEICDGGELNE = .{ + .cur_story_buffs = story_buff, + }, + }; + ChallengeData.challenge_mode = 1; + } else if (challenge_id > 30000) { + var boss_buff = protocol.ChallengeBossBuffList{ + .buff_list = ArrayList(u32).init(self.allocator), + .challenge_boss_const = 1, + }; + try boss_buff.buff_list.append(challengeConf.maze_buff_id); + try boss_buff.buff_list.append(buff_id); + try challenge_blessing_list.appendSlice(boss_buff.buff_list.items); + cur_challenge_info.stage_info = .{ + .IEICDGGELNE = .{ + .cur_boss_buffs = boss_buff, + }, + }; + ChallengeData.challenge_mode = 2; + } + ChallengeData.challenge_floorID = entrance.floor_id; + ChallengeData.challenge_worldID = maze.world_id; + ChallengeData.challenge_monsterID = challengeConf.npc_monster_id_list2.items[challengeConf.npc_monster_id_list2.items.len - 1]; + ChallengeData.challenge_eventID = challengeConf.event_id_list2.items[challengeConf.event_id_list2.items.len - 1]; + ChallengeData.challenge_groupID = id; + ChallengeData.challenge_maze_groupID = id; + ChallengeData.challenge_planeID = maze.challenge_plane_id; + ChallengeData.challenge_entryID = challengeConf.map_entrance_id2; + } else { + std.debug.print("THIS CHALLENGE ID: {} DOES NOT SUPPORT 2ND NODE. PLEASE DO COMMAND /node TO SWITCH BACK TO FIRST NODE\n", .{challenge_id}); + } + } + } + } + } + } + } + } + ChallengeData.challenge_blessing = challenge_blessing_list.items[0..challenge_blessing_list.items.len]; + ChallengeData.challenge_stageID = ChallengeData.challenge_eventID; + return cur_challenge_info; + } +}; diff --git a/gameserver/src/manager/lineup_mgr.zig b/gameserver/src/manager/lineup_mgr.zig index 65465df..b7dc9f7 100644 --- a/gameserver/src/manager/lineup_mgr.zig +++ b/gameserver/src/manager/lineup_mgr.zig @@ -4,6 +4,9 @@ const Session = @import("../Session.zig"); const Packet = @import("../Packet.zig"); const Config = @import("../services/config.zig"); const Data = @import("../data.zig"); +const ChallengeData = @import("../services/challenge.zig"); +const NodeCheck = @import("../commands/value.zig"); +const BattleManager = @import("../manager/battle_mgr.zig"); const UidGenerator = @import("../services/item.zig").UidGenerator; const ArrayList = std.ArrayList; @@ -24,6 +27,9 @@ pub const LineupManager = struct { lineup.name = .{ .Const = "CipherSR" }; for (config.avatar_config.items, 0..) |avatarConf, idx| { + if (idx >= 4) { + break; + } var avatar = protocol.LineupAvatar.init(self.allocator); avatar.id = avatarConf.id; avatar.slot = @intCast(idx); @@ -33,7 +39,67 @@ pub const LineupManager = struct { avatar.avatar_type = protocol.AvatarType.AVATAR_FORMAL_TYPE; try lineup.avatar_list.append(avatar); } - + var id_list = try self.allocator.alloc(u32, config.avatar_config.items.len); + defer self.allocator.free(id_list); + for (config.avatar_config.items, 0..) |slot, idx| { + if (idx >= 4) { + break; + } + id_list[idx] = slot.id; + } + try getSelectedAvatarID(self.allocator, id_list); return lineup; } }; + +pub const ChallengeLineupManager = struct { + allocator: std.mem.Allocator, + pub fn init(allocator: std.mem.Allocator) ChallengeLineupManager { + return ChallengeLineupManager{ .allocator = allocator }; + } + pub fn createLineup( + self: *ChallengeLineupManager, + avatar_list: ArrayList(u32), + ) !protocol.LineupInfo { + var lineup = protocol.LineupInfo.init(self.allocator); + lineup.mp = 5; + lineup.max_mp = 5; + lineup.extra_lineup_type = if (NodeCheck.challenge_node == 0) protocol.ExtraLineupType.LINEUP_CHALLENGE else protocol.ExtraLineupType.LINEUP_CHALLENGE_2; + + for (avatar_list.items, 0..) |avatarlist, idx| { + var avatar = protocol.LineupAvatar.init(self.allocator); + avatar.id = avatarlist; + avatar.slot = @intCast(idx); + avatar.satiety = 0; + avatar.hp = 10000; + avatar.sp_bar = .{ .sp_cur = 10000, .sp_max = 10000 }; + avatar.avatar_type = protocol.AvatarType.AVATAR_FORMAL_TYPE; + try lineup.avatar_list.append(avatar); + } + var id_list = try self.allocator.alloc(u32, avatar_list.items.len); + defer self.allocator.free(id_list); + for (avatar_list.items, 0..) |slot, idx| { + if (idx >= 4) { + break; + } + id_list[idx] = slot; + } + try getSelectedAvatarID(self.allocator, id_list); + return lineup; + } +}; + +pub fn getSelectedAvatarID(allocator: std.mem.Allocator, input: []const u32) !void { + var tempList = std.ArrayList(u32).init(allocator); + defer tempList.deinit(); + + try tempList.appendSlice(input); + + var i: usize = 0; + while (i < BattleManager.selectedAvatarID.len and i < tempList.items.len) : (i += 1) { + BattleManager.selectedAvatarID[i] = tempList.items[i]; + } + while (i < BattleManager.selectedAvatarID.len) : (i += 1) { + BattleManager.selectedAvatarID[i] = 0; + } +} diff --git a/gameserver/src/manager/scene_mgr.zig b/gameserver/src/manager/scene_mgr.zig index cfd6d13..af4c737 100644 --- a/gameserver/src/manager/scene_mgr.zig +++ b/gameserver/src/manager/scene_mgr.zig @@ -29,6 +29,7 @@ pub const SceneManager = struct { const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json"); var generator = UidGenerator().init(); var scene_info = protocol.SceneInfo.init(self.allocator); + scene_info.game_mode_type = 1; scene_info.plane_id = plane_id; scene_info.floor_id = floor_id; scene_info.entry_id = entry_id; @@ -37,6 +38,7 @@ pub const SceneManager = struct { scene_info.client_pos_version = 1; var group_map = std.AutoHashMap(u32, protocol.SceneEntityGroupInfo).init(self.allocator); defer group_map.deinit(); + for (res_config.scene_config.items) |sceneConf| { for (sceneConf.teleports.items) |teleConf| { if (teleConf.teleportId == teleport_id) { @@ -100,6 +102,27 @@ pub const SceneManager = struct { var iter = group_map.iterator(); while (iter.next()) |entry| { try scene_info.entity_group_list.append(entry.value_ptr.*); + try scene_info.entity_list.appendSlice(entry.value_ptr.entity_list.items); + try scene_info.DJBIBIJMEBH.append(entry.value_ptr.group_id); + try scene_info.custom_data_list.append(protocol.CustomSaveData{ + .group_id = entry.value_ptr.group_id, + }); + try scene_info.group_state_list.append(protocol.SceneGroupState{ + .group_id = entry.value_ptr.group_id, + .state = 0, + .is_default = true, + }); + } + const ranges = [_][2]usize{ + .{ 0, 101 }, + .{ 10000, 10051 }, + .{ 20000, 20001 }, + .{ 30000, 30020 }, + }; + for (ranges) |range| { + for (range[0]..range[1]) |i| { + try scene_info.lighten_section_list.append(@intCast(i)); + } } return scene_info; } diff --git a/gameserver/src/services/avatar.zig b/gameserver/src/services/avatar.zig index 568f19a..f16eb7f 100644 --- a/gameserver/src/services/avatar.zig +++ b/gameserver/src/services/avatar.zig @@ -182,3 +182,10 @@ pub fn onGetBigData(session: *Session, packet: *const Packet, allocator: Allocat rsp.EIGPMIBCIKG = req.EIGPMIBCIKG; try session.send(CmdID.CmdGetBigDataRecommendScRsp, rsp); } +pub fn onGetPreAvatarGrowthInfo(session: *Session, packet: *const Packet, allocator: Allocator) !void { + const req = try packet.getProto(protocol.GetPreAvatarGrowthInfoCsReq, allocator); + var rsp = protocol.GetPreAvatarGrowthInfoScRsp.init(allocator); + rsp.retcode = 0; + rsp.KJAEOJBJOJD = req.KJAEOJBJOJD; + try session.send(CmdID.CmdGetPreAvatarGrowthInfoScRsp, rsp); +} diff --git a/gameserver/src/services/battle.zig b/gameserver/src/services/battle.zig index b129524..0d4e8bf 100644 --- a/gameserver/src/services/battle.zig +++ b/gameserver/src/services/battle.zig @@ -7,15 +7,19 @@ const Data = @import("../data.zig"); const ChallengeData = @import("challenge.zig"); const BattleManager = @import("../manager/battle_mgr.zig").BattleManager; const ChallegeStageManager = @import("../manager/battle_mgr.zig").ChallegeStageManager; +const NodeCheck = @import("../commands/value.zig"); const ArrayList = std.ArrayList; const Allocator = std.mem.Allocator; const CmdID = protocol.CmdID; +pub var on_battle: bool = false; + pub fn onStartCocoonStage(session: *Session, packet: *const Packet, allocator: Allocator) !void { const req = try packet.getProto(protocol.StartCocoonStageCsReq, allocator); var battle_mgr = BattleManager.init(allocator); const battle = try battle_mgr.createBattle(); + on_battle = true; try session.send(CmdID.CmdStartCocoonStageScRsp, protocol.StartCocoonStageScRsp{ .retcode = 0, .cocoon_id = req.cocoon_id, @@ -28,6 +32,7 @@ pub fn onQuickStartCocoonStage(session: *Session, packet: *const Packet, allocat const req = try packet.getProto(protocol.QuickStartCocoonStageCsReq, allocator); var battle_mgr = BattleManager.init(allocator); const battle = try battle_mgr.createBattle(); + on_battle = true; try session.send(CmdID.CmdQuickStartCocoonStageScRsp, protocol.QuickStartCocoonStageScRsp{ .retcode = 0, .cocoon_id = req.cocoon_id, @@ -39,6 +44,7 @@ pub fn onQuickStartFarmElement(session: *Session, packet: *const Packet, allocat const req = try packet.getProto(protocol.QuickStartFarmElementCsReq, allocator); var battle_mgr = BattleManager.init(allocator); const battle = try battle_mgr.createBattle(); + on_battle = true; try session.send(CmdID.CmdQuickStartFarmElementScRsp, protocol.QuickStartFarmElementScRsp{ .retcode = 0, .world_level = req.world_level, @@ -50,6 +56,7 @@ pub fn onStartBattleCollege(session: *Session, packet: *const Packet, allocator: const req = try packet.getProto(protocol.StartBattleCollegeCsReq, allocator); var battle_mgr = BattleManager.init(allocator); const battle = try battle_mgr.createBattle(); + on_battle = true; try session.send(CmdID.CmdStartBattleCollegeScRsp, protocol.StartBattleCollegeScRsp{ .retcode = 0, .id = req.id, @@ -69,23 +76,47 @@ pub fn onSceneCastSkill(session: *Session, packet: *const Packet, allocator: All .monster_battle_type = protocol.MonsterBattleType.MONSTER_BATTLE_TYPE_TRIGGER_BATTLE, }, }); + + var battle_info: ?protocol.SceneBattleInfo = null; + + if (req.assist_monster_entity_id_list.items.len > 0 or + (req.attacked_by_entity_id >= 1 and req.attacked_by_entity_id <= 99)) + { + if (ChallengeData.on_challenge) { + battle_info = challenge; + } else { + battle_info = battle; + on_battle = true; + } + } + try session.send(CmdID.CmdSceneCastSkillScRsp, protocol.SceneCastSkillScRsp{ .retcode = 0, .cast_entity_id = req.cast_entity_id, .monster_battle_info = monster_battle_info_list, - .battle_info = if (req.assist_monster_entity_id_list.items.len > 0 or - (req.attacked_by_entity_id >= 1 and req.attacked_by_entity_id <= 99)) - (if (ChallengeData.on_challenge) challenge else battle) - else - null, + .battle_info = battle_info, }); } +pub fn onGetCurBattleInfo(session: *Session, _: *const Packet, allocator: Allocator) !void { + var battle_mgr = BattleManager.init(allocator); + const battle = try battle_mgr.createBattle(); + var challege_mgr = ChallegeStageManager.init(allocator); + const challenge = try challege_mgr.createChallegeStage(); + + var rsp = protocol.GetCurBattleInfoScRsp.init(allocator); + rsp.battle_info = if (ChallengeData.on_challenge == true) challenge else if (on_battle == true) battle else null; + rsp.retcode = 0; + try session.send(CmdID.CmdGetCurBattleInfoScRsp, rsp); +} + pub fn onPVEBattleResult(session: *Session, packet: *const Packet, allocator: Allocator) !void { const req = try packet.getProto(protocol.PVEBattleResultCsReq, allocator); var rsp = protocol.PVEBattleResultScRsp.init(allocator); rsp.battle_id = req.battle_id; rsp.end_status = req.end_status; + rsp.stage_id = req.stage_id; + on_battle = false; try session.send(CmdID.CmdPVEBattleResultScRsp, rsp); } @@ -96,3 +127,12 @@ pub fn onSceneCastSkillCostMp(session: *Session, packet: *const Packet, allocato .cast_entity_id = req.cast_entity_id, }); } + +pub fn onSyncClientResVersion(session: *Session, packet: *const Packet, allocator: Allocator) !void { + const req = try packet.getProto(protocol.SyncClientResVersionCsReq, allocator); + std.debug.print("CLIENT RES VERSION: {}\n", .{req.client_res_version}); + try session.send(CmdID.CmdSyncClientResVersionScRsp, protocol.SyncClientResVersionScRsp{ + .retcode = 0, + .client_res_version = req.client_res_version, + }); +} diff --git a/gameserver/src/services/challenge.zig b/gameserver/src/services/challenge.zig index 3a487b1..812694f 100644 --- a/gameserver/src/services/challenge.zig +++ b/gameserver/src/services/challenge.zig @@ -6,9 +6,11 @@ const Config = @import("config.zig"); const Res_config = @import("res_config.zig"); const Data = @import("../data.zig"); const ChallegeStageManager = @import("../manager/battle_mgr.zig").ChallegeStageManager; +const ChallengeManager = @import("../manager/challenge_mgr.zig").ChallengeManager; const SceneManager = @import("../manager/scene_mgr.zig").SceneManager; const ChallengeSceneManager = @import("../manager/scene_mgr.zig").ChallengeSceneManager; const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager; +const ChallengeLineupManager = @import("../manager/lineup_mgr.zig").ChallengeLineupManager; const NodeCheck = @import("../commands/value.zig"); const ArrayList = std.ArrayList; @@ -40,13 +42,45 @@ fn contains(list: *const std.ArrayListAligned(u32, null), value: u32) bool { } return false; } -pub var challenge_stageID: u32 = 0; -pub var challenge_mode: u32 = 0; - pub var on_challenge: bool = false; pub const ChallengeBlessing = ArrayList(u32); pub var challenge_blessing: []const u32 = &.{}; +pub var challenge_mode: u32 = 0; + +pub var challenge_planeID: u32 = 0; +pub var challenge_floorID: u32 = 0; +pub var challenge_entryID: u32 = 0; +pub var challenge_worldID: u32 = 0; +pub var challenge_monsterID: u32 = 0; +pub var challenge_eventID: u32 = 0; +pub var challenge_groupID: u32 = 0; +pub var challenge_maze_groupID: u32 = 0; +pub var challenge_stageID: u32 = 0; + +pub var challengeID: u32 = 0; +pub var challenge_buffID: u32 = 0; + +pub const ChallengeAvatarList = ArrayList(u32); +pub var avatar_list: ChallengeAvatarList = ChallengeAvatarList.init(std.heap.page_allocator); + +pub fn resetChallengeState() void { + on_challenge = false; + challenge_mode = 0; + challenge_planeID = 0; + challenge_floorID = 0; + challenge_entryID = 0; + challenge_worldID = 0; + challenge_monsterID = 0; + challenge_eventID = 0; + challenge_groupID = 0; + challenge_maze_groupID = 0; + challenge_stageID = 0; + challengeID = 0; + challenge_buffID = 0; + challenge_blessing = &.{}; + _ = avatar_list.clearRetainingCapacity(); +} pub fn onGetChallenge(session: *Session, _: *const Packet, allocator: Allocator) !void { const challenge_config = try Config.loadChallengeConfig(allocator, "resources/ChallengeMazeConfig.json"); @@ -71,7 +105,6 @@ pub fn onGetChallengeGroupStatistics(session: *Session, packet: *const Packet, a var rsp = protocol.GetChallengeGroupStatisticsScRsp.init(allocator); rsp.retcode = 0; rsp.group_id = req.group_id; - try session.send(CmdID.CmdGetChallengeGroupStatisticsScRsp, rsp); } pub fn onLeaveChallenge(session: *Session, _: *const Packet, allocator: Allocator) !void { @@ -85,196 +118,99 @@ pub fn onLeaveChallenge(session: *Session, _: *const Packet, allocator: Allocato .lineup = lineup, .scene = scene_info, }); - on_challenge = false; + resetChallengeState(); challenge_mode = 0; try session.send(CmdID.CmdLeaveChallengeScRsp, protocol.LeaveChallengeScRsp{ .retcode = 0, }); } +pub fn onGetCurChallengeScRsp(session: *Session, _: *const Packet, allocator: Allocator) !void { + var rsp = protocol.GetCurChallengeScRsp.init(allocator); + var lineup_manager = ChallengeLineupManager.init(allocator); + const lineup_info = try lineup_manager.createLineup(avatar_list); + var challenge_manager = ChallengeManager.init(allocator); + const cur_challenge_info = try challenge_manager.createChallenge( + challengeID, + challenge_buffID, + ); + rsp.retcode = 0; + if (on_challenge == true) { + rsp.cur_challenge = cur_challenge_info; + try rsp.lineup_list.append(lineup_info); + + std.debug.print("CURRENT CHALLENGE STAGE ID:{}\n", .{challenge_stageID}); + std.debug.print("CURRENT CHALLENGE LINEUP AVATAR ID:{}\n", .{avatar_list}); + std.debug.print("CURRENT CHALLENGE MONSTER ID:{}\n", .{challenge_monsterID}); + if (challenge_mode == 0) { + std.debug.print("CURRENT CHALLENGE: {} MOC\n", .{challenge_mode}); + } else if (challenge_mode == 1) { + std.debug.print("CURRENT CHALLENGE: {} PF\n", .{challenge_mode}); + std.debug.print("CURRENT CHALLENGE STAGE BLESSING ID:{}, SELECTED BLESSING ID:{}\n", .{ challenge_blessing[0], challenge_blessing[1] }); + } else { + std.debug.print("CURRENT CHALLENGE: {} AS\n", .{challenge_mode}); + std.debug.print("CURRENT CHALLENGE STAGE BLESSING ID:{}, SELECTED BLESSING ID:{}\n", .{ challenge_blessing[0], challenge_blessing[1] }); + } + } else { + std.debug.print("CURRENT ON CHALLENGE STATE: {}\n", .{on_challenge}); + } + + try session.send(CmdID.CmdGetCurChallengeScRsp, rsp); +} pub fn onStartChallenge(session: *Session, packet: *const Packet, allocator: Allocator) !void { - var challenge_blessing_list = ChallengeBlessing{ - .allocator = std.heap.page_allocator, - .items = &.{}, - .capacity = 0, - }; const req = try packet.getProto(protocol.StartChallengeCsReq, allocator); var rsp = protocol.StartChallengeScRsp.init(allocator); - const challenge_config = try Config.loadChallengeConfig(allocator, "resources/ChallengeMazeConfig.json"); - const entrance_config = try Config.loadMapEntranceConfig(allocator, "resources/MapEntrance.json"); - const maze_config = try Config.loadMazePlaneConfig(allocator, "resources/MazePlane.json"); - var avatarList = std.ArrayList(u32).init(allocator); - var lineup = protocol.LineupInfo.init(allocator); - lineup.mp = 5; - lineup.max_mp = 5; - lineup.extra_lineup_type = if (NodeCheck.challenge_node == 0) protocol.ExtraLineupType.LINEUP_CHALLENGE else protocol.ExtraLineupType.LINEUP_CHALLENGE_2; + challengeID = req.challenge_id; if (NodeCheck.challenge_node == 0) { - for (req.first_lineup.items, 0..) |avt, idx| { - var list = protocol.LineupAvatar.init(allocator); - try avatarList.append(avt); - list.id = avt; - list.slot = @intCast(idx); - list.satiety = 0; - list.avatar_type = protocol.AvatarType.AVATAR_FORMAL_TYPE; - list.hp = 10000; - list.sp_bar = .{ .sp_cur = 10000, .sp_max = 10000 }; - try lineup.avatar_list.append(list); + for (req.first_lineup.items) |id| { + try avatar_list.append(id); } - try rsp.lineup_list.append(lineup); + if (challengeID > 20000 and challengeID < 30000) + challenge_buffID = req.stage_info.?.IEICDGGELNE.?.story_info.buff_one; + if (challengeID > 30000) + challenge_buffID = req.stage_info.?.IEICDGGELNE.?.boss_info.buff_one; } else { - for (req.second_lineup.items, 0..) |avt, idx| { - var list = protocol.LineupAvatar.init(allocator); - try avatarList.append(avt); - list.id = avt; - list.slot = @intCast(idx); - list.satiety = 0; - list.avatar_type = protocol.AvatarType.AVATAR_FORMAL_TYPE; - list.hp = 10000; - list.sp_bar = .{ .sp_cur = 10000, .sp_max = 10000 }; - try lineup.avatar_list.append(list); + for (req.second_lineup.items) |id| { + try avatar_list.append(id); } - try rsp.lineup_list.append(lineup); + if (challengeID > 20000 and challengeID < 30000) + challenge_buffID = req.stage_info.?.IEICDGGELNE.?.story_info.buff_two; + if (challengeID > 30000) + challenge_buffID = req.stage_info.?.IEICDGGELNE.?.boss_info.buff_two; } + var lineup_manager = ChallengeLineupManager.init(allocator); + const lineup_info = try lineup_manager.createLineup(avatar_list); - var cur_challenge_info = protocol.CurChallenge.init(allocator); - cur_challenge_info.challenge_id = req.challenge_id; - cur_challenge_info.score_id = if (req.challenge_id > 20000 and req.challenge_id < 30000) 40000 else 0; - cur_challenge_info.score_two = 0; - cur_challenge_info.status = protocol.ChallengeStatus.CHALLENGE_DOING; - cur_challenge_info.extra_lineup_type = if (NodeCheck.challenge_node == 0) protocol.ExtraLineupType.LINEUP_CHALLENGE else protocol.ExtraLineupType.LINEUP_CHALLENGE_2; - var planeID: u32 = 0; - var floorID: u32 = 0; - var entryID: u32 = 0; - var worldID: u32 = 0; - var monsterID: u32 = 0; - var eventID: u32 = 0; - var groupID: u32 = 0; - var maze_groupID: u32 = 0; - if (NodeCheck.challenge_node == 0) { - for (challenge_config.challenge_config.items) |challengeConf| { - if (challengeConf.id == req.challenge_id) { - std.debug.print("TRACING CONFIG ID {} WITH CHALLENGE ID {}\n", .{ challengeConf.id, req.challenge_id }); - for (entrance_config.map_entrance_config.items) |entrance| { - if (entrance.id == challengeConf.map_entrance_id) { - for (maze_config.maze_plane_config.items) |maze| { - if (contains(&maze.floor_id_list, entrance.floor_id)) { - if (req.challenge_id > 20000 and req.challenge_id < 30000) { - var story_buff = protocol.ChallengeStoryBuffList{ - .buff_list = ArrayList(u32).init(allocator), - }; - try story_buff.buff_list.append(challengeConf.maze_buff_id); - try story_buff.buff_list.append(req.stage_info.?.IEICDGGELNE.?.story_info.buff_one); - try challenge_blessing_list.appendSlice(story_buff.buff_list.items); - cur_challenge_info.stage_info = .{ - .IEICDGGELNE = .{ - .cur_story_buffs = story_buff, - }, - }; - challenge_mode = 1; - } else if (req.challenge_id > 30000) { - var boss_buff = protocol.ChallengeBossBuffList{ - .buff_list = ArrayList(u32).init(allocator), - .challenge_boss_const = 1, - }; - try boss_buff.buff_list.append(challengeConf.maze_buff_id); - try boss_buff.buff_list.append(req.stage_info.?.IEICDGGELNE.?.boss_info.buff_one); - try challenge_blessing_list.appendSlice(boss_buff.buff_list.items); - cur_challenge_info.stage_info = .{ - .IEICDGGELNE = .{ - .cur_boss_buffs = boss_buff, - }, - }; - challenge_mode = 2; - } - floorID = entrance.floor_id; - worldID = maze.world_id; - monsterID = challengeConf.npc_monster_id_list1.items[challengeConf.npc_monster_id_list1.items.len - 1]; - eventID = challengeConf.event_id_list1.items[challengeConf.event_id_list1.items.len - 1]; - groupID = challengeConf.maze_group_id1; - maze_groupID = challengeConf.maze_group_id1; - planeID = maze.challenge_plane_id; - entryID = challengeConf.map_entrance_id; - } - } - } - } - } - } - } else { - for (challenge_config.challenge_config.items) |challengeConf| { - if (challengeConf.id == req.challenge_id) { - std.debug.print("TRACING CONFIG ID {} WITH CHALLENGE ID {}\n", .{ challengeConf.id, req.challenge_id }); - for (entrance_config.map_entrance_config.items) |entrance| { - if (entrance.id == challengeConf.map_entrance_id2) { - for (maze_config.maze_plane_config.items) |maze| { - if (contains(&maze.floor_id_list, entrance.floor_id)) { - if (challengeConf.maze_group_id2) |id| { - if (req.challenge_id > 20000 and req.challenge_id < 30000) { - var story_buff = protocol.ChallengeStoryBuffList{ - .buff_list = ArrayList(u32).init(allocator), - }; - try story_buff.buff_list.append(challengeConf.maze_buff_id); - try story_buff.buff_list.append(req.stage_info.?.IEICDGGELNE.?.story_info.buff_two); - try challenge_blessing_list.appendSlice(story_buff.buff_list.items); - cur_challenge_info.stage_info = .{ - .IEICDGGELNE = .{ - .cur_story_buffs = story_buff, - }, - }; - challenge_mode = 1; - } else if (req.challenge_id > 30000) { - var boss_buff = protocol.ChallengeBossBuffList{ - .buff_list = ArrayList(u32).init(allocator), - .challenge_boss_const = 1, - }; - try boss_buff.buff_list.append(challengeConf.maze_buff_id); - try boss_buff.buff_list.append(req.stage_info.?.IEICDGGELNE.?.boss_info.buff_two); - try challenge_blessing_list.appendSlice(boss_buff.buff_list.items); - cur_challenge_info.stage_info = .{ - .IEICDGGELNE = .{ - .cur_boss_buffs = boss_buff, - }, - }; - challenge_mode = 2; - } - floorID = entrance.floor_id; - worldID = maze.world_id; - monsterID = challengeConf.npc_monster_id_list2.items[challengeConf.npc_monster_id_list2.items.len - 1]; - eventID = challengeConf.event_id_list2.items[challengeConf.event_id_list2.items.len - 1]; - groupID = id; - maze_groupID = id; - planeID = maze.challenge_plane_id; - entryID = challengeConf.map_entrance_id2; - } else { - std.debug.print("THIS CHALLENGE ID: {} DOES NOT SUPPORT 2ND NODE. PLEASE DO COMMAND /node TO SWITCH BACK TO FIRST NODE\n", .{req.challenge_id}); - } - } - } - } - } - } - } - } - var scene_PF_manager = ChallengeSceneManager.init(allocator); - const scene_info = try scene_PF_manager.createScene( - avatarList, - planeID, - floorID, - entryID, - worldID, - monsterID, - eventID, - groupID, - maze_groupID, + var challenge_manager = ChallengeManager.init(allocator); + const cur_challenge_info = try challenge_manager.createChallenge( + challengeID, + challenge_buffID, + ); + var scene_challenge_manager = ChallengeSceneManager.init(allocator); + const scene_info = try scene_challenge_manager.createScene( + avatar_list, + challenge_planeID, + challenge_floorID, + challenge_entryID, + challenge_worldID, + challenge_monsterID, + challenge_eventID, + challenge_groupID, + challenge_maze_groupID, ); rsp.retcode = 0; rsp.scene = scene_info; rsp.cur_challenge = cur_challenge_info; + try rsp.lineup_list.append(lineup_info); on_challenge = true; - challenge_stageID = eventID; - challenge_blessing = challenge_blessing_list.items[0..challenge_blessing_list.items.len]; try session.send(CmdID.CmdStartChallengeScRsp, rsp); - std.debug.print("SEND PLANE ID {} FLOOR ID {} ENTRY ID {} GROUP ID {} MAZE GROUP ID {}\n", .{ planeID, floorID, entryID, groupID, maze_groupID }); + std.debug.print("SEND PLANE ID {} FLOOR ID {} ENTRY ID {} GROUP ID {} MAZE GROUP ID {}\n", .{ + challenge_planeID, + challenge_floorID, + challenge_entryID, + challenge_groupID, + challenge_maze_groupID, + }); } diff --git a/gameserver/src/services/config.zig b/gameserver/src/services/config.zig index 4cb4ef7..ec6a1ac 100644 --- a/gameserver/src/services/config.zig +++ b/gameserver/src/services/config.zig @@ -3,7 +3,7 @@ const ArrayList = std.ArrayList; const std = @import("std"); -const BattleConfig = struct { +pub const BattleConfig = struct { battle_id: u32, stage_id: u32, cycle_count: u32, @@ -11,7 +11,7 @@ const BattleConfig = struct { monster_level: u32, blessings: ArrayList(u32), }; -const Stage = struct { +pub const Stage = struct { level: u32, stage_id: u32, monster_list: ArrayList(ArrayList(u32)), @@ -47,7 +47,7 @@ pub const Relic = struct { step4: u32, }; -const Avatar = struct { +pub const Avatar = struct { id: u32, hp: u32, sp: u32, @@ -353,10 +353,7 @@ fn parseChallengeConfig(root: std.json.Value, allocator: Allocator) ErrorSet!Cha .map_entrance_id = @intCast(challenge_json.object.get("MapEntranceID").?.integer), .map_entrance_id2 = @intCast(challenge_json.object.get("MapEntranceID2").?.integer), .maze_group_id1 = @intCast(challenge_json.object.get("MazeGroupID1").?.integer), - .maze_group_id2 = if (challenge_json.object.get("MazeGroupID2")) |val| - @intCast(val.integer) - else - null, + .maze_group_id2 = if (challenge_json.object.get("MazeGroupID2")) |val| @intCast(val.integer) else null, }; for (challenge_json.object.get("NpcMonsterIDList1").?.array.items) |npc1| { try challenge.npc_monster_id_list1.append(@intCast(npc1.integer)); @@ -435,15 +432,15 @@ fn parseRelic(relic_str: []const u8, allocator: Allocator) !Relic { const tokens_slice = tokens.items; - if (tokens_slice.len < 8) { - std.debug.print("relic parsing error: {s}\n", .{relic_str}); + if (tokens_slice.len < 5) { + std.debug.print("relic parsing critical error (too few fields): {s}\n", .{relic_str}); return error.InsufficientTokens; } const stat1 = try parseStatCount(tokens_slice[4]); - const stat2 = try parseStatCount(tokens_slice[5]); - const stat3 = try parseStatCount(tokens_slice[6]); - const stat4 = try parseStatCount(tokens_slice[7]); + const stat2 = if (tokens_slice.len > 5) try parseStatCount(tokens_slice[5]) else StatCount{ .stat = 0, .count = 0, .step = 0 }; + const stat3 = if (tokens_slice.len > 6) try parseStatCount(tokens_slice[6]) else StatCount{ .stat = 0, .count = 0, .step = 0 }; + const stat4 = if (tokens_slice.len > 7) try parseStatCount(tokens_slice[7]) else StatCount{ .stat = 0, .count = 0, .step = 0 }; const relic = Relic{ .id = try std.fmt.parseInt(u32, tokens_slice[0], 10), diff --git a/gameserver/src/services/item.zig b/gameserver/src/services/item.zig index 54251ca..a4102e7 100644 --- a/gameserver/src/services/item.zig +++ b/gameserver/src/services/item.zig @@ -5,6 +5,7 @@ const Packet = @import("../Packet.zig"); const Config = @import("config.zig"); const Data = @import("../data.zig"); const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager; +const Sync = @import("../commands/sync.zig"); const ArrayList = std.ArrayList; const Allocator = std.mem.Allocator; const CmdID = protocol.CmdID; @@ -12,7 +13,6 @@ const CmdID = protocol.CmdID; pub fn onGetBag(session: *Session, _: *const Packet, allocator: Allocator) !void { const config = try Config.loadGameConfig(allocator, "config.json"); var generator = UidGenerator().init(); - // fake item inventory // TODO: make real one var rsp = protocol.GetBagScRsp.init(allocator); @@ -81,7 +81,9 @@ pub fn UidGenerator() type { pub fn init() Self { return Self{ .current_id = 0 }; } - + pub fn curId(self: *const Self) u32 { + return self.current_id; + } pub fn nextId(self: *Self) u32 { self.current_id +%= 1; // Using wrapping addition return self.current_id; diff --git a/gameserver/src/services/lineup.zig b/gameserver/src/services/lineup.zig index bdb8c60..bf44fbc 100644 --- a/gameserver/src/services/lineup.zig +++ b/gameserver/src/services/lineup.zig @@ -3,13 +3,14 @@ const protocol = @import("protocol"); const Session = @import("../Session.zig"); const Packet = @import("../Packet.zig"); const Config = @import("config.zig"); -const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager; +const LineupManager = @import("../manager/lineup_mgr.zig"); +const BattleManager = @import("../manager/battle_mgr.zig"); const Allocator = std.mem.Allocator; const CmdID = protocol.CmdID; pub fn onGetCurLineupData(session: *Session, _: *const Packet, allocator: Allocator) !void { - var lineup_mgr = LineupManager.init(allocator); + var lineup_mgr = LineupManager.LineupManager.init(allocator); const lineup = try lineup_mgr.createLineup(); try session.send(CmdID.CmdGetCurLineupDataScRsp, protocol.GetCurLineupDataScRsp{ .retcode = 0, @@ -42,6 +43,17 @@ pub fn onReplaceLineup(session: *Session, packet: *const Packet, allocator: Allo }; try lineup.avatar_list.append(avatar); } + + var id_list = try allocator.alloc(u32, req.lineup_slot_list.items.len); + defer allocator.free(id_list); + for (req.lineup_slot_list.items, 0..) |slot, idx| { + if (idx >= 4) { + break; + } + id_list[idx] = slot.id; + } + try LineupManager.getSelectedAvatarID(allocator, id_list); + var rsp = protocol.SyncLineupNotify.init(allocator); rsp.Lineup = lineup; try session.send(CmdID.CmdSyncLineupNotify, rsp); diff --git a/gameserver/src/services/scene.zig b/gameserver/src/services/scene.zig index f239321..a707b9a 100644 --- a/gameserver/src/services/scene.zig +++ b/gameserver/src/services/scene.zig @@ -89,6 +89,7 @@ pub fn onGetSceneMapInfo(session: *Session, packet: *const Packet, allocator: Al rsp.retcode = 0; rsp.content_id = req.content_id; rsp.entry_story_line_id = req.entry_story_line_id; + rsp.IGFIKGHLLNO = true; var map_info = protocol.SceneMapInfo.init(allocator); try map_info.chest_list.appendSlice(chest_list); map_info.entry_id = @intCast(floor_id); @@ -146,6 +147,27 @@ pub fn onEnterSection(session: *Session, packet: *const Packet, allocator: Alloc std.debug.print("ENTER SECTION Id: {}\n", .{req.section_id}); try session.send(CmdID.CmdEnterSectionScRsp, rsp); } + +pub fn onGetEnteredScene(session: *Session, _: *const Packet, allocator: Allocator) !void { + var rsp = protocol.GetEnteredSceneScRsp.init(allocator); + var noti = protocol.EnteredSceneChangeScNotify.init(allocator); + + const entrance_config = try Config.loadMapEntranceConfig(allocator, "resources/MapEntrance.json"); + for (entrance_config.map_entrance_config.items) |entrance| { + try rsp.entered_scene_info_list.append(protocol.EnteredSceneInfo{ + .floor_id = entrance.floor_id, + .plane_id = entrance.plane_id, + }); + try noti.entered_scene_info_list.append(protocol.EnteredSceneInfo{ + .floor_id = entrance.floor_id, + .plane_id = entrance.plane_id, + }); + } + rsp.retcode = 0; + try session.send(CmdID.CmdEnteredSceneChangeScNotify, noti); + try session.send(CmdID.CmdGetEnteredSceneScRsp, rsp); +} + pub fn onSceneEntityTeleport(session: *Session, packet: *const Packet, allocator: Allocator) !void { const req = try packet.getProto(protocol.SceneEntityTeleportCsReq, allocator); var rsp = protocol.SceneEntityTeleportScRsp.init(allocator); diff --git a/hotfix.json b/hotfix.json index 3561381..9bc294d 100644 --- a/hotfix.json +++ b/hotfix.json @@ -17,10 +17,18 @@ }, "OSBETAWin3.2.53": { "asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_10104345_a5535dc0c779_a36b870100988f", - "ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_10114007_b191beeb9546_2f88552ee48ab1", + "ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_10128229_1abd7f8c3047_3cb3e1bffdb636", "ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_0_40d2ce0253_6d871f8bca6eb4", "ifix_version": "0", "lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_10104505_31a450a7e7a3_0793e9a5426e39", "lua_version": "" + }, + "OSBETAWin3.2.54": { + "asset_bundle_url": "https://autopatchos.starrails.com/asb/BetaLive/output_10182178_c0cbb41bb650_9e7401ee501684", + "ex_resource_url": "https://autopatchos.starrails.com/design_data/BetaLive/output_10190349_65c2a054220b_3d06b35cecd7a9", + "ifix_url": "https://autopatchos.starrails.com/ifix/BetaLive/output_10208090_dc0587466a83_7fc79b628127d3", + "ifix_version": "0", + "lua_url": "https://autopatchos.starrails.com/lua/BetaLive/output_10182439_b0ec3d3bcfc2_d9d2a226df4dd2", + "lua_version": "" } } \ No newline at end of file