Update to v3:
- better respond time - import BuffInfoConfig.json - implemented custom mode for challenge mode to sellect older MoC/PF/AS
This commit is contained in:
parent
8043986612
commit
ca7eec0745
19 changed files with 1511 additions and 326 deletions
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
### Setup launcher.exe
|
### Setup launcher.exe
|
||||||
|
|
||||||
Copy and paste launcher.exe and hkprg.dll from launcher folder inside hysilens-sr to client folder.
|
Copy and paste launcher.exe and hkprg.dll from [launcher folder](https://git.xeondev.com/HonkaiSlopRail/hysilens-sr/src/branch/main/launcher) inside hysilens-sr to `client folder`.
|
||||||
|
|
||||||
### From source
|
### From source
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ cd hysilens-sr
|
||||||
zig build run-dispatch
|
zig build run-dispatch
|
||||||
zig build run-gameserver
|
zig build run-gameserver
|
||||||
```
|
```
|
||||||
Then open your launcher.exe with administrator.
|
Then open your `launcher.exe` with administrator.
|
||||||
|
|
||||||
### Using Pre-built Binaries
|
### Using Pre-built Binaries
|
||||||
Navigate to the [Releases](https://git.xeondev.com/HonkaiSlopRail/hysilens-sr/releases)
|
Navigate to the [Releases](https://git.xeondev.com/HonkaiSlopRail/hysilens-sr/releases)
|
||||||
|
@ -33,7 +33,7 @@ page and download the latest release for your platform.
|
||||||
## Functionality (work in progress)
|
## Functionality (work in progress)
|
||||||
- Login and player spawn
|
- Login and player spawn
|
||||||
- Test battle via calyx
|
- Test battle via calyx
|
||||||
- MOC/PF/AS simulator
|
- MOC/PF/AS simulator with custom stage sellection
|
||||||
- Gacha simulator
|
- Gacha simulator
|
||||||
- Support command for Sillyism
|
- Support command for Sillyism
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
|
// gameserver/src/Session.zig
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const protocol = @import("protocol");
|
const protocol = @import("protocol");
|
||||||
const handlers = @import("handlers.zig");
|
const handlers = @import("handlers.zig");
|
||||||
const Packet = @import("Packet.zig");
|
const Packet = @import("Packet.zig");
|
||||||
|
const Cache = @import("../src/manager/scene_mgr.zig");
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
|
||||||
|
|
||||||
const Stream = std.net.Stream;
|
const Stream = std.net.Stream;
|
||||||
const Address = std.net.Address;
|
const Address = std.net.Address;
|
||||||
|
|
||||||
|
@ -15,12 +16,22 @@ const log = std.log.scoped(.session);
|
||||||
address: Address,
|
address: Address,
|
||||||
stream: Stream,
|
stream: Stream,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
main_allocator: Allocator,
|
||||||
|
game_config_cache: *Cache.GameConfigCache,
|
||||||
|
|
||||||
pub fn init(address: Address, stream: Stream, allocator: Allocator) Self {
|
pub fn init(
|
||||||
|
address: Address,
|
||||||
|
stream: Stream,
|
||||||
|
session_allocator: Allocator,
|
||||||
|
main_allocator: Allocator,
|
||||||
|
game_config_cache: *Cache.GameConfigCache,
|
||||||
|
) Self {
|
||||||
return .{
|
return .{
|
||||||
.address = address,
|
.address = address,
|
||||||
.stream = stream,
|
.stream = stream,
|
||||||
.allocator = allocator,
|
.allocator = session_allocator,
|
||||||
|
.main_allocator = main_allocator,
|
||||||
|
.game_config_cache = game_config_cache,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +42,6 @@ pub fn run(self: *Self) !void {
|
||||||
while (true) {
|
while (true) {
|
||||||
var packet = Packet.read(&reader, self.allocator) catch break;
|
var packet = Packet.read(&reader, self.allocator) catch break;
|
||||||
defer packet.deinit();
|
defer packet.deinit();
|
||||||
|
|
||||||
try handlers.handle(self, &packet);
|
try handlers.handle(self, &packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ const commandList = [_]Command{
|
||||||
Command{ .name = "unstuck", .action = "", .func = unstuck_command.handle },
|
Command{ .name = "unstuck", .action = "", .func = unstuck_command.handle },
|
||||||
Command{ .name = "sync", .action = "", .func = sync_command.onGenerateAndSync },
|
Command{ .name = "sync", .action = "", .func = sync_command.onGenerateAndSync },
|
||||||
Command{ .name = "refill", .action = "", .func = refill_command.onRefill },
|
Command{ .name = "refill", .action = "", .func = refill_command.onRefill },
|
||||||
|
Command{ .name = "id", .action = "", .func = value_command.onBuffId },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn handleCommand(session: *Session, msg: []const u8, allocator: Allocator) Error!void {
|
pub fn handleCommand(session: *Session, msg: []const u8, allocator: Allocator) Error!void {
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub fn handle(session: *Session, _: []const u8, allocator: Allocator) Error!void
|
||||||
try commandhandler.sendMessage(session, "/refill to refill technique point after battle\n", allocator);
|
try commandhandler.sendMessage(session, "/refill to refill technique point after battle\n", allocator);
|
||||||
try commandhandler.sendMessage(session, "/set to set gacha banner\n", allocator);
|
try commandhandler.sendMessage(session, "/set to set gacha banner\n", allocator);
|
||||||
try commandhandler.sendMessage(session, "/node to chage node in PF, AS, MoC\n", allocator);
|
try commandhandler.sendMessage(session, "/node to chage node in PF, AS, MoC\n", allocator);
|
||||||
|
try commandhandler.sendMessage(session, "/id to turn ON custom mode for challenge mode. /id info to check current challenge id. /id off to turn OFF\n", allocator);
|
||||||
try commandhandler.sendMessage(session, "You can enter MoC, PF, AS via F4 menu\n", allocator);
|
try commandhandler.sendMessage(session, "You can enter MoC, PF, AS via F4 menu\n", allocator);
|
||||||
try commandhandler.sendMessage(session, "(If your Castorice technique enabled, you must enter battle by using Castorice's technique)\n", allocator);
|
try commandhandler.sendMessage(session, "(If your Castorice technique enabled, you must enter battle by using Castorice's technique)\n", allocator);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const commandhandler = @import("../command.zig");
|
const commandhandler = @import("../command.zig");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Session = @import("../Session.zig");
|
const Session = @import("../Session.zig");
|
||||||
|
const Config = @import("../services/config.zig");
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Error = commandhandler.Error;
|
const Error = commandhandler.Error;
|
||||||
|
@ -10,6 +11,11 @@ pub var StandardBanner = [_]u32{ 1003, 1004, 1101, 1104, 1209, 1211 };
|
||||||
pub var RateUp = [_]u32{1410};
|
pub var RateUp = [_]u32{1410};
|
||||||
pub var RateUpFourStars = [_]u32{ 1210, 1108, 1207 };
|
pub var RateUpFourStars = [_]u32{ 1210, 1108, 1207 };
|
||||||
|
|
||||||
|
pub var selected_challenge_id: u32 = 0;
|
||||||
|
pub var selected_buff_id: u32 = 0;
|
||||||
|
|
||||||
|
pub var custom_mode: bool = false;
|
||||||
|
|
||||||
pub fn handle(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
pub fn handle(session: *Session, _: []const u8, allocator: Allocator) Error!void {
|
||||||
try commandhandler.sendMessage(session, "Test Command for Chat\n", allocator);
|
try commandhandler.sendMessage(session, "Test Command for Chat\n", allocator);
|
||||||
}
|
}
|
||||||
|
@ -122,3 +128,166 @@ fn sendErrorMessage(session: *Session, message: []const u8, allocator: Allocator
|
||||||
fn isValidAvatarId(avatar_id: u32) bool {
|
fn isValidAvatarId(avatar_id: u32) bool {
|
||||||
return avatar_id >= 1000 and avatar_id <= 9999;
|
return avatar_id >= 1000 and avatar_id <= 9999;
|
||||||
}
|
}
|
||||||
|
pub fn onBuffId(session: *Session, input: []const u8, allocator: Allocator) !void {
|
||||||
|
if (std.ascii.eqlIgnoreCase(std.mem.trim(u8, input, " "), "info")) {
|
||||||
|
return try onBuffInfo(session, allocator);
|
||||||
|
}
|
||||||
|
var tokens = std.ArrayList([]const u8).init(allocator);
|
||||||
|
defer tokens.deinit();
|
||||||
|
var iter = std.mem.tokenize(u8, input, " ");
|
||||||
|
while (iter.next()) |tok| {
|
||||||
|
try tokens.append(tok);
|
||||||
|
}
|
||||||
|
if (tokens.items.len == 0) {
|
||||||
|
return sendErrorMessage(session, "Error: Missing command arguments.", allocator);
|
||||||
|
}
|
||||||
|
if (std.ascii.eqlIgnoreCase(tokens.items[0], "off")) {
|
||||||
|
custom_mode = false;
|
||||||
|
_ = try commandhandler.sendMessage(session, "Custom mode OFF.", allocator);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tokens.items.len < 6) {
|
||||||
|
return sendErrorMessage(session, "Error: Usage: /id <group_id> floor <n> node <1|2>", allocator);
|
||||||
|
}
|
||||||
|
const group_id = std.fmt.parseInt(u32, tokens.items[0], 10) catch return sendErrorMessage(session, "Error: Invalid group ID.", allocator);
|
||||||
|
if (!std.ascii.eqlIgnoreCase(tokens.items[1], "floor")) return sendErrorMessage(session, "Error: Expected 'floor' keyword.", allocator);
|
||||||
|
const floor = std.fmt.parseInt(u32, tokens.items[2], 10) catch return sendErrorMessage(session, "Error: Invalid floor number.", allocator);
|
||||||
|
if (!std.ascii.eqlIgnoreCase(tokens.items[3], "node")) return sendErrorMessage(session, "Error: Expected 'node' keyword.", allocator);
|
||||||
|
const node = std.fmt.parseInt(u8, tokens.items[4], 10) catch return sendErrorMessage(session, "Error: Invalid node number.", allocator);
|
||||||
|
if (node != 1 and node != 2) return sendErrorMessage(session, "Error: Node must be 1 or 2.", allocator);
|
||||||
|
challenge_node = node - 1;
|
||||||
|
const challenge_mode = switch (group_id / 1000) {
|
||||||
|
1 => "MoC",
|
||||||
|
2 => "PF",
|
||||||
|
3 => "AS",
|
||||||
|
else => "Unknown",
|
||||||
|
};
|
||||||
|
try commandhandler.sendMessage(session, try std.fmt.allocPrint(allocator, "Challenge mode: {s}", .{challenge_mode}), allocator);
|
||||||
|
const challenge_config = try Config.loadChallengeConfig(allocator, "resources/ChallengeMazeConfig.json");
|
||||||
|
const stage_config = try Config.loadStageConfig(allocator, "resources/StageConfig.json");
|
||||||
|
const challenge_entry = for (challenge_config.challenge_config.items) |entry| {
|
||||||
|
if (entry.group_id == group_id and entry.floor == floor)
|
||||||
|
break entry;
|
||||||
|
} else {
|
||||||
|
return sendErrorMessage(session, "Error: Could not find matching challenge ID.", allocator);
|
||||||
|
};
|
||||||
|
if (tokens.items.len > 5) {
|
||||||
|
const keyword = tokens.items[5];
|
||||||
|
if (std.ascii.eqlIgnoreCase(keyword, "buff")) {
|
||||||
|
if (tokens.items.len < 7) {
|
||||||
|
return sendErrorMessage(session, "Error: Missing buff sub-command (info/set <index>).", allocator);
|
||||||
|
}
|
||||||
|
const sub = tokens.items[6];
|
||||||
|
if (std.ascii.eqlIgnoreCase(sub, "info")) {
|
||||||
|
return sendBuffInfo(session, allocator, group_id, node);
|
||||||
|
} else if (std.ascii.eqlIgnoreCase(sub, "set")) {
|
||||||
|
return sendErrorMessage(session, "Error: Missing buff index for 'set' command.", allocator);
|
||||||
|
} else {
|
||||||
|
const buff_index = std.fmt.parseInt(usize, sub, 10) catch {
|
||||||
|
return sendErrorMessage(session, "Error: Invalid buff index.", allocator);
|
||||||
|
};
|
||||||
|
if (tokens.items.len < 8 or !std.ascii.eqlIgnoreCase(tokens.items[7], "set")) {
|
||||||
|
return sendErrorMessage(session, "Error: Expected 'set' after buff index.", allocator);
|
||||||
|
}
|
||||||
|
return handleBuffSetCommand(session, allocator, group_id, node, buff_index, challenge_entry.id);
|
||||||
|
}
|
||||||
|
} else if (std.ascii.eqlIgnoreCase(keyword, "set")) {
|
||||||
|
if ((group_id / 1000) != 1) {
|
||||||
|
return sendErrorMessage(session, "Error: Unexpected 'set' command. Did you mean 'buff <index> set' ?", allocator);
|
||||||
|
}
|
||||||
|
try handleMoCSelectChallenge(session, allocator, challenge_entry.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var event_id: ?u32 = null;
|
||||||
|
if (node == 1 and challenge_entry.event_id_list1.items.len > 0) {
|
||||||
|
event_id = challenge_entry.event_id_list1.items[0];
|
||||||
|
} else if (node == 2 and challenge_entry.event_id_list2.items.len > 0) {
|
||||||
|
event_id = challenge_entry.event_id_list2.items[0];
|
||||||
|
}
|
||||||
|
if (event_id == null) {
|
||||||
|
return sendErrorMessage(session, "Error: Could not find matching EventID.", allocator);
|
||||||
|
}
|
||||||
|
if ((group_id / 1000) == 1) {
|
||||||
|
try handleMoCSelectChallenge(session, allocator, challenge_entry.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (stage_config.stage_config.items) |stage| {
|
||||||
|
if (stage.stage_id == event_id.?) {
|
||||||
|
try sendStageInfo(session, allocator, group_id, floor, node, stage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sendErrorMessage(session, "Error: Stage not found for given EventID.", allocator);
|
||||||
|
}
|
||||||
|
fn handleMoCSelectChallenge(session: *Session, allocator: Allocator, challenge_id: u32) !void {
|
||||||
|
const line = try std.fmt.allocPrint(allocator, "Selected MoC Challenge ID: {d}", .{challenge_id});
|
||||||
|
try commandhandler.sendMessage(session, line, allocator);
|
||||||
|
selected_challenge_id = challenge_id;
|
||||||
|
selected_buff_id = 0;
|
||||||
|
custom_mode = true;
|
||||||
|
}
|
||||||
|
fn handleBuffSetCommand(session: *Session, allocator: Allocator, group_id: u32, node: u8, buff_index: usize, challenge_id: u32) !void {
|
||||||
|
const buff_config = try Config.loadTextMapConfig(allocator, "resources/BuffInfoConfig.json");
|
||||||
|
for (buff_config.text_map_config.items) |entry| {
|
||||||
|
if (entry.group_id == group_id) {
|
||||||
|
const list = if (node == 1) &entry.buff_list1 else &entry.buff_list2;
|
||||||
|
if (buff_index == 0 or buff_index > list.items.len) {
|
||||||
|
return sendErrorMessage(session, "Error: Buff index out of range.", allocator);
|
||||||
|
}
|
||||||
|
const buff = list.items[buff_index - 1];
|
||||||
|
const line = try std.fmt.allocPrint(allocator, "Selected Challenge ID: {d}, Buff ID: {d} - {s}", .{ challenge_id, buff.id, buff.name });
|
||||||
|
try commandhandler.sendMessage(session, line, allocator);
|
||||||
|
selected_challenge_id = challenge_id;
|
||||||
|
selected_buff_id = buff.id;
|
||||||
|
custom_mode = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sendErrorMessage(session, "Error: Buff group ID not found.", allocator);
|
||||||
|
}
|
||||||
|
fn sendStageInfo(session: *Session, allocator: Allocator, group_id: u32, floor: u32, node: u8, stage: Config.Stage) !void {
|
||||||
|
const header = try std.fmt.allocPrint(allocator, "GroupID: {d}, Floor: {d}, Node: {d}, StageID: {d}", .{ group_id, floor, node, stage.stage_id });
|
||||||
|
try commandhandler.sendMessage(session, header, allocator);
|
||||||
|
for (stage.monster_list.items, 0..) |wave, i| {
|
||||||
|
var msg = try std.fmt.allocPrint(allocator, "wave {d}:", .{i + 1});
|
||||||
|
for (wave.items) |monster_id| {
|
||||||
|
msg = try std.fmt.allocPrint(allocator, "{s} {d},", .{ msg, monster_id });
|
||||||
|
}
|
||||||
|
try commandhandler.sendMessage(session, msg, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn sendBuffInfo(session: *Session, allocator: Allocator, group_id: u32, node: u8) !void {
|
||||||
|
const buff_config = try Config.loadTextMapConfig(allocator, "resources/BuffInfoConfig.json");
|
||||||
|
for (buff_config.text_map_config.items) |entry| {
|
||||||
|
if (entry.group_id == group_id) {
|
||||||
|
const list = if (node == 1) &entry.buff_list1 else &entry.buff_list2;
|
||||||
|
for (list.items) |buff| {
|
||||||
|
const line = try std.fmt.allocPrint(allocator, "id: {d} - {s}", .{ buff.id, buff.name });
|
||||||
|
try commandhandler.sendMessage(session, line, allocator);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sendErrorMessage(session, "Error: Buff group ID not found.", allocator);
|
||||||
|
}
|
||||||
|
pub fn onBuffInfo(session: *Session, allocator: Allocator) !void {
|
||||||
|
const challenge_config = try Config.loadChallengeConfig(allocator, "resources/ChallengeMazeConfig.json");
|
||||||
|
|
||||||
|
var max_moc: u32 = 0;
|
||||||
|
var max_pf: u32 = 0;
|
||||||
|
var max_as: u32 = 0;
|
||||||
|
|
||||||
|
for (challenge_config.challenge_config.items) |entry| {
|
||||||
|
const id = entry.group_id;
|
||||||
|
if (id >= 1000 and id < 2000 and id > max_moc) {
|
||||||
|
max_moc = id;
|
||||||
|
} else if (id >= 2000 and id < 3000 and id > max_pf) {
|
||||||
|
max_pf = id;
|
||||||
|
} else if (id >= 3000 and id < 4000 and id > max_as) {
|
||||||
|
max_as = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const msg = try std.fmt.allocPrint(allocator, "Current Challenge IDs: MoC: {d}, PF: {d}, AS: {d}", .{ max_moc, max_pf, max_as });
|
||||||
|
try commandhandler.sendMessage(session, msg, allocator);
|
||||||
|
}
|
||||||
|
|
|
@ -30,22 +30,6 @@ pub const AllAvatars = &[_]u32{
|
||||||
1406, 1407, 1408, 1409, 1410, 1412,
|
1406, 1407, 1408, 1409, 1410, 1412,
|
||||||
};
|
};
|
||||||
// Battle group
|
// Battle group
|
||||||
//TODO: update every patch
|
|
||||||
pub const buffs_unlocked = &[_]u32{
|
|
||||||
100101, 100201, 100301, 100401, 100501, 100601, 100801, 100901, 101301,
|
|
||||||
110101, 110201, 110202, 110203, 110301, 110401, 110501, 110601, 110701,
|
|
||||||
110801, 110901, 111001, 111101, 111201, 120101, 120301, 120401, 120501,
|
|
||||||
120601, 120701, 120702, 120801, 120802, 120901, 121001, 121101, 121201,
|
|
||||||
121202, 121203, 121301, 121302, 121303, 121401, 121501, 121701, 121801,
|
|
||||||
122001, 122002, 122003, 122004, 122101, 122201, 122301, 122302, 122303,
|
|
||||||
122304, 122402, 122403, 122501, 130101, 130201, 130301, 130302, 130303,
|
|
||||||
130401, 130402, 130403, 130404, 130405, 130406, 130501, 130601, 130602,
|
|
||||||
130701, 130801, 130802, 130803, 130901, 130902, 130903, 131001, 131002,
|
|
||||||
131201, 131301, 131401, 131501, 131502, 131503, 131701, 131702, 140101,
|
|
||||||
140102, 140201, 140202, 140301, 140401, 140501, 140601, 140701, 140801,
|
|
||||||
140901, 200501, 200601, 220501, 221201, 800301, 800501, 101401, 101501,
|
|
||||||
141001,
|
|
||||||
};
|
|
||||||
|
|
||||||
//TODO: update more Remembrance character in future
|
//TODO: update more Remembrance character in future
|
||||||
pub const Rem = [_]u32{ 8007, 8008, 1402, 1407, 1409 };
|
pub const Rem = [_]u32{ 8007, 8008, 1402, 1407, 1409 };
|
||||||
|
|
|
@ -26,7 +26,7 @@ const CmdID = protocol.CmdID;
|
||||||
const log = std.log.scoped(.handlers);
|
const log = std.log.scoped(.handlers);
|
||||||
|
|
||||||
const Action = *const fn (*Session, *const Packet, Allocator) anyerror!void;
|
const Action = *const fn (*Session, *const Packet, Allocator) anyerror!void;
|
||||||
const HandlerList = [_]struct { CmdID, Action }{
|
pub const HandlerList = [_]struct { CmdID, Action }{
|
||||||
.{ CmdID.CmdPlayerGetTokenCsReq, login.onPlayerGetToken },
|
.{ CmdID.CmdPlayerGetTokenCsReq, login.onPlayerGetToken },
|
||||||
.{ CmdID.CmdPlayerLoginCsReq, login.onPlayerLogin },
|
.{ CmdID.CmdPlayerLoginCsReq, login.onPlayerLogin },
|
||||||
.{ CmdID.CmdPlayerHeartBeatCsReq, misc.onPlayerHeartBeat },
|
.{ CmdID.CmdPlayerHeartBeatCsReq, misc.onPlayerHeartBeat },
|
||||||
|
@ -233,6 +233,7 @@ const DummyCmdList = [_]struct { CmdID, CmdID }{
|
||||||
.{ CmdID.CmdChimeraGetDataCsReq, CmdID.CmdChimeraGetDataScRsp },
|
.{ CmdID.CmdChimeraGetDataCsReq, CmdID.CmdChimeraGetDataScRsp },
|
||||||
.{ CmdID.CmdMarbleGetDataCsReq, CmdID.CmdMarbleGetDataScRsp },
|
.{ CmdID.CmdMarbleGetDataCsReq, CmdID.CmdMarbleGetDataScRsp },
|
||||||
.{ CmdID.CmdGetPreAvatarActivityListCsReq, CmdID.CmdGetPreAvatarActivityListScRsp },
|
.{ CmdID.CmdGetPreAvatarActivityListCsReq, CmdID.CmdGetPreAvatarActivityListScRsp },
|
||||||
|
.{ CmdID.CmdGetArchiveDataCsReq, CmdID.CmdGetArchiveDataScRsp },
|
||||||
};
|
};
|
||||||
|
|
||||||
const SuppressLogList = [_]CmdID{CmdID.CmdSceneEntityMoveCsReq};
|
const SuppressLogList = [_]CmdID{CmdID.CmdSceneEntityMoveCsReq};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const network = @import("network.zig");
|
const network = @import("network.zig");
|
||||||
const handlers = @import("handlers.zig");
|
const Cache = @import("../src/manager/scene_mgr.zig");
|
||||||
|
|
||||||
pub const std_options = .{
|
pub const std_options = .{
|
||||||
.log_level = switch (builtin.mode) {
|
.log_level = switch (builtin.mode) {
|
||||||
|
@ -11,5 +11,11 @@ pub const std_options = .{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
try Cache.initGameGlobals(allocator);
|
||||||
|
defer Cache.deinitGameGlobals();
|
||||||
try network.listen();
|
try network.listen();
|
||||||
|
std.log.info("Server listening for connections.", .{});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const protocol = @import("protocol");
|
const protocol = @import("protocol");
|
||||||
const Session = @import("../Session.zig");
|
|
||||||
const Packet = @import("../Packet.zig");
|
|
||||||
const Config = @import("../services/config.zig");
|
const Config = @import("../services/config.zig");
|
||||||
const Data = @import("../data.zig");
|
const Data = @import("../data.zig");
|
||||||
const Lineup = @import("../services/lineup.zig");
|
const Lineup = @import("../services/lineup.zig");
|
||||||
const ChallengeData = @import("../services/challenge.zig");
|
const ChallengeData = @import("../services/challenge.zig");
|
||||||
const NodeCheck = @import("../commands/value.zig");
|
const NodeCheck = @import("../commands/value.zig");
|
||||||
|
const scene_managers = @import("./scene_mgr.zig");
|
||||||
|
|
||||||
const ArrayList = std.ArrayList;
|
const ArrayList = std.ArrayList;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
@ -14,7 +13,6 @@ const CmdID = protocol.CmdID;
|
||||||
|
|
||||||
pub var selectedAvatarID = [_]u32{ 1304, 1313, 1406, 1004 };
|
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 {
|
fn isInList(id: u32, list: []const u32) bool {
|
||||||
for (list) |item| {
|
for (list) |item| {
|
||||||
if (item == id) {
|
if (item == id) {
|
||||||
|
@ -34,6 +32,7 @@ const Element = enum {
|
||||||
Imaginary,
|
Imaginary,
|
||||||
None,
|
None,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn getAvatarElement(avatar_id: u32) Element {
|
fn getAvatarElement(avatar_id: u32) Element {
|
||||||
return switch (avatar_id) {
|
return switch (avatar_id) {
|
||||||
1105, 1107, 1111, 1206, 1215, 1221, 1302, 1309, 1315, 1408, 1410, 8001, 8002 => .Physical,
|
1105, 1107, 1111, 1206, 1215, 1221, 1302, 1309, 1315, 1408, 1410, 8001, 8002 => .Physical,
|
||||||
|
@ -46,6 +45,7 @@ fn getAvatarElement(avatar_id: u32) Element {
|
||||||
else => .None,
|
else => .None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getAttackerBuffId() u32 {
|
fn getAttackerBuffId() u32 {
|
||||||
const avatar_id = selectedAvatarID[Lineup.leader_slot];
|
const avatar_id = selectedAvatarID[Lineup.leader_slot];
|
||||||
const element = getAvatarElement(avatar_id);
|
const element = getAvatarElement(avatar_id);
|
||||||
|
@ -60,19 +60,16 @@ fn getAttackerBuffId() u32 {
|
||||||
.None => 0,
|
.None => 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
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{
|
var relic = protocol.BattleRelic{ .id = id, .main_affix_id = main_affix_id, .level = level, .sub_affix_list = ArrayList(protocol.RelicAffix).init(allocator) };
|
||||||
.id = id,
|
if (stat1 != 0) try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat1, .cnt = cnt1, .step = step1 });
|
||||||
.main_affix_id = main_affix_id,
|
if (stat2 != 0) try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat2, .cnt = cnt2, .step = step2 });
|
||||||
.level = level,
|
if (stat3 != 0) try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat3, .cnt = cnt3, .step = step3 });
|
||||||
.sub_affix_list = ArrayList(protocol.RelicAffix).init(allocator),
|
if (stat4 != 0) try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat4, .cnt = cnt4, .step = step4 });
|
||||||
};
|
|
||||||
try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat1, .cnt = cnt1, .step = step1 });
|
|
||||||
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;
|
return relic;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createBattleAvatar(allocator: Allocator, avatarConf: Config.Avatar) !protocol.BattleAvatar {
|
fn createBattleAvatar(allocator: Allocator, avatarConf: Config.Avatar) !protocol.BattleAvatar {
|
||||||
var avatar = protocol.BattleAvatar.init(allocator);
|
var avatar = protocol.BattleAvatar.init(allocator);
|
||||||
avatar.id = avatarConf.id;
|
avatar.id = avatarConf.id;
|
||||||
|
@ -84,13 +81,11 @@ fn createBattleAvatar(allocator: Allocator, avatarConf: Config.Avatar) !protocol
|
||||||
avatar.avatar_type = .AVATAR_FORMAL_TYPE;
|
avatar.avatar_type = .AVATAR_FORMAL_TYPE;
|
||||||
if (isInList(avatar.id, &Data.EnhanceAvatarID)) avatar.enhanced_id = 1;
|
if (isInList(avatar.id, &Data.EnhanceAvatarID)) avatar.enhanced_id = 1;
|
||||||
|
|
||||||
// Relics
|
|
||||||
for (avatarConf.relics.items) |relic| {
|
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);
|
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);
|
try avatar.relic_list.append(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lightcone
|
|
||||||
const lc = protocol.BattleEquipment{
|
const lc = protocol.BattleEquipment{
|
||||||
.id = avatarConf.lightcone.id,
|
.id = avatarConf.lightcone.id,
|
||||||
.rank = avatarConf.lightcone.rank,
|
.rank = avatarConf.lightcone.rank,
|
||||||
|
@ -99,7 +94,6 @@ fn createBattleAvatar(allocator: Allocator, avatarConf: Config.Avatar) !protocol
|
||||||
};
|
};
|
||||||
try avatar.equipment_list.append(lc);
|
try avatar.equipment_list.append(lc);
|
||||||
|
|
||||||
// Skills
|
|
||||||
var talentLevel: u32 = 0;
|
var talentLevel: u32 = 0;
|
||||||
const skill_list: []const u32 = if (isInList(avatar.id, &Data.Rem)) &Data.skills else &Data.skills_old;
|
const skill_list: []const u32 = if (isInList(avatar.id, &Data.Rem)) &Data.skills else &Data.skills_old;
|
||||||
for (skill_list) |elem| {
|
for (skill_list) |elem| {
|
||||||
|
@ -117,27 +111,114 @@ fn createBattleAvatar(allocator: Allocator, avatarConf: Config.Avatar) !protocol
|
||||||
return avatar;
|
return avatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to add technique buffs
|
const BuffRule = struct {
|
||||||
|
avatar_id: u32,
|
||||||
|
buffs: []const struct {
|
||||||
|
id: u32,
|
||||||
|
owner_is_avatar: bool = true,
|
||||||
|
dynamic_values: []const protocol.BattleBuff.DynamicValuesEntry = &.{},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const technique_buffs = [_]BuffRule{
|
||||||
|
.{
|
||||||
|
.avatar_id = 1208,
|
||||||
|
.buffs = &.{
|
||||||
|
.{ .id = 120801, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
.{ .id = 120802, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.avatar_id = 1224,
|
||||||
|
.buffs = &.{
|
||||||
|
.{ .id = 122402, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
.{ .id = 122403, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
.{ .id = 122401, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "#ADF_1" }, .value = 3 },
|
||||||
|
.{ .key = .{ .Const = "#ADF_2" }, .value = 3 },
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.avatar_id = 1304,
|
||||||
|
.buffs = &.{.{ .id = 130403, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 3 },
|
||||||
|
} }},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.avatar_id = 1306,
|
||||||
|
.buffs = &.{
|
||||||
|
.{ .id = 130601, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
.{ .id = 130602, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.avatar_id = 1309,
|
||||||
|
.buffs = &.{
|
||||||
|
.{ .id = 130901, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
.{ .id = 130902, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.avatar_id = 1310,
|
||||||
|
.buffs = &.{
|
||||||
|
.{ .id = 131001, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
.{ .id = 131002, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
.{ .id = 1000112, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.avatar_id = 1412,
|
||||||
|
.buffs = &.{
|
||||||
|
.{ .id = 141201, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "SkillIndex" }, .value = 0 },
|
||||||
|
} },
|
||||||
|
.{ .id = 141202, .owner_is_avatar = false, .dynamic_values = &.{
|
||||||
|
.{ .key = .{ .Const = "#ADF_1" }, .value = 1 },
|
||||||
|
.{ .key = .{ .Const = "#ADF_2" }, .value = 1 },
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
fn addTechniqueBuffs(allocator: Allocator, battle: *protocol.SceneBattleInfo, avatar: protocol.BattleAvatar, avatarConf: Config.Avatar, avatar_index: u32) !void {
|
fn addTechniqueBuffs(allocator: Allocator, battle: *protocol.SceneBattleInfo, avatar: protocol.BattleAvatar, avatarConf: Config.Avatar, avatar_index: u32) !void {
|
||||||
if (!avatarConf.use_technique) return;
|
if (!avatarConf.use_technique) return;
|
||||||
|
|
||||||
var targetIndexList = ArrayList(u32).init(allocator);
|
var targetIndexList = ArrayList(u32).init(allocator);
|
||||||
try targetIndexList.append(0);
|
try targetIndexList.append(0);
|
||||||
|
|
||||||
var buffedAvatarId = avatar.id;
|
const buffedAvatarId = switch (avatar.id) {
|
||||||
if (avatar.id == 8004) {
|
8004 => 8003,
|
||||||
buffedAvatarId = 8003;
|
8006 => 8005,
|
||||||
} else if (avatar.id == 8006) {
|
8008 => 8007,
|
||||||
buffedAvatarId = 8005;
|
else => avatar.id,
|
||||||
} else if (avatar.id == 8008) {
|
};
|
||||||
buffedAvatarId = 8007;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Data.buffs_unlocked) |buffId| {
|
if (isInList(buffedAvatarId, &Data.IgnoreToughness)) {
|
||||||
const idPrefix = buffId / 100;
|
|
||||||
if (idPrefix == buffedAvatarId) {
|
|
||||||
var buff = protocol.BattleBuff{
|
var buff = protocol.BattleBuff{
|
||||||
.id = buffId,
|
.id = 1000119,
|
||||||
.level = 1,
|
.level = 1,
|
||||||
.owner_index = @intCast(avatar_index),
|
.owner_index = @intCast(avatar_index),
|
||||||
.wave_flag = 0xFFFFFFFF,
|
.wave_flag = 0xFFFFFFFF,
|
||||||
|
@ -147,93 +228,42 @@ fn addTechniqueBuffs(allocator: Allocator, battle: *protocol.SceneBattleInfo, av
|
||||||
try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
||||||
try battle.buff_list.append(buff);
|
try battle.buff_list.append(buff);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isInList(buffedAvatarId, &Data.IgnoreToughness)) {
|
var found_custom_buff = false;
|
||||||
var buff_tough = protocol.BattleBuff{
|
for (technique_buffs) |rule| {
|
||||||
.id = 1000119, //for is_ignore toughness
|
if (rule.avatar_id == buffedAvatarId) {
|
||||||
|
found_custom_buff = true;
|
||||||
|
for (rule.buffs) |buff_def| {
|
||||||
|
var buff = protocol.BattleBuff{
|
||||||
|
.id = buff_def.id,
|
||||||
|
.level = 1,
|
||||||
|
.owner_index = if (buff_def.owner_is_avatar) @intCast(avatar_index) else Lineup.leader_slot,
|
||||||
|
.wave_flag = 0xFFFFFFFF,
|
||||||
|
.target_index_list = targetIndexList,
|
||||||
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator),
|
||||||
|
};
|
||||||
|
try buff.dynamic_values.appendSlice(buff_def.dynamic_values);
|
||||||
|
try battle.buff_list.append(buff);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found_custom_buff) {
|
||||||
|
var buff = protocol.BattleBuff{
|
||||||
|
.id = buffedAvatarId * 100 + 1,
|
||||||
.level = 1,
|
.level = 1,
|
||||||
.owner_index = @intCast(avatar_index),
|
.owner_index = @intCast(avatar_index),
|
||||||
.wave_flag = 0xFFFFFFFF,
|
.wave_flag = 0xFFFFFFFF,
|
||||||
.target_index_list = targetIndexList,
|
.target_index_list = targetIndexList,
|
||||||
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator),
|
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator),
|
||||||
};
|
};
|
||||||
try buff_tough.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
||||||
try battle.buff_list.append(buff_tough);
|
try battle.buff_list.append(buff);
|
||||||
}
|
|
||||||
|
|
||||||
if (buffedAvatarId == 1224) {
|
|
||||||
var buff_march = protocol.BattleBuff{
|
|
||||||
.id = 122401, //for hunt march 7th tech
|
|
||||||
.level = 1,
|
|
||||||
.owner_index = @intCast(avatar_index),
|
|
||||||
.wave_flag = 0xFFFFFFFF,
|
|
||||||
.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 = 0xFFFFFFFF,
|
|
||||||
.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 = 0xFFFFFFFF,
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffedAvatarId == 1412) {
|
|
||||||
var buff_ce = protocol.BattleBuff{
|
|
||||||
.id = 141201, //for cerydra core buff
|
|
||||||
.level = 1,
|
|
||||||
.owner_index = @intCast(avatar_index),
|
|
||||||
.wave_flag = 0xFFFFFFFF,
|
|
||||||
.target_index_list = targetIndexList,
|
|
||||||
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator),
|
|
||||||
};
|
|
||||||
try buff_ce.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
|
|
||||||
var buff_target = protocol.BattleBuff{
|
|
||||||
.id = 141202, //for switch leader
|
|
||||||
.level = 1,
|
|
||||||
.owner_index = Lineup.leader_slot,
|
|
||||||
.wave_flag = 0xFFFFFFFF,
|
|
||||||
.target_index_list = targetIndexList,
|
|
||||||
.dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator),
|
|
||||||
};
|
|
||||||
try buff_target.dynamic_values.appendSlice(&[_]protocol.BattleBuff.DynamicValuesEntry{
|
|
||||||
.{ .key = .{ .Const = "1" }, .value = 1 },
|
|
||||||
.{ .key = .{ .Const = "2" }, .value = 1 },
|
|
||||||
});
|
|
||||||
try battle.buff_list.append(buff_ce);
|
|
||||||
try battle.buff_list.append(buff_target);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to add future global buff.
|
|
||||||
fn addGolbalPassive(allocator: Allocator, battle: *protocol.SceneBattleInfo) !void {
|
fn addGolbalPassive(allocator: Allocator, battle: *protocol.SceneBattleInfo) !void {
|
||||||
if (isInList(1407, Data.AllAvatars)) { //support Castorice
|
if (isInList(1407, Data.AllAvatars)) {
|
||||||
var targetIndexList = ArrayList(u32).init(allocator);
|
var targetIndexList = ArrayList(u32).init(allocator);
|
||||||
try targetIndexList.append(0);
|
try targetIndexList.append(0);
|
||||||
var mazebuff_data = protocol.BattleBuff{
|
var mazebuff_data = protocol.BattleBuff{
|
||||||
|
@ -249,7 +279,6 @@ fn addGolbalPassive(allocator: Allocator, battle: *protocol.SceneBattleInfo) !vo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to add elemental hit when trigger battle .
|
|
||||||
fn addTriggerAttack(allocator: Allocator, battle: *protocol.SceneBattleInfo) !void {
|
fn addTriggerAttack(allocator: Allocator, battle: *protocol.SceneBattleInfo) !void {
|
||||||
var targetIndexList = ArrayList(u32).init(allocator);
|
var targetIndexList = ArrayList(u32).init(allocator);
|
||||||
try targetIndexList.append(0);
|
try targetIndexList.append(0);
|
||||||
|
@ -264,6 +293,7 @@ fn addTriggerAttack(allocator: Allocator, battle: *protocol.SceneBattleInfo) !vo
|
||||||
try attack.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 1 });
|
try attack.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 1 });
|
||||||
try battle.buff_list.append(attack);
|
try battle.buff_list.append(attack);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createBattleInfo(allocator: Allocator, config: Config.GameConfig, stage_monster_wave_len: u32, stage_id: u32, rounds_limit: u32) protocol.SceneBattleInfo {
|
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);
|
var battle = protocol.SceneBattleInfo.init(allocator);
|
||||||
battle.battle_id = config.battle_config.battle_id;
|
battle.battle_id = config.battle_config.battle_id;
|
||||||
|
@ -274,7 +304,8 @@ fn createBattleInfo(allocator: Allocator, config: Config.GameConfig, stage_monst
|
||||||
battle.world_level = 6;
|
battle.world_level = 6;
|
||||||
return battle;
|
return battle;
|
||||||
}
|
}
|
||||||
fn addMonsterWaves(allocator: Allocator, battle: *protocol.SceneBattleInfo, monster_wave_configs: std.ArrayList(std.ArrayList(u32)), monster_level: u32) !void { // Added monster_level
|
|
||||||
|
fn addMonsterWaves(allocator: Allocator, battle: *protocol.SceneBattleInfo, monster_wave_configs: std.ArrayList(std.ArrayList(u32)), monster_level: u32) !void {
|
||||||
for (monster_wave_configs.items) |wave| {
|
for (monster_wave_configs.items) |wave| {
|
||||||
var monster_wave = protocol.SceneMonsterWave.init(allocator);
|
var monster_wave = protocol.SceneMonsterWave.init(allocator);
|
||||||
monster_wave.monster_param = protocol.SceneMonsterWaveParam{ .level = monster_level };
|
monster_wave.monster_param = protocol.SceneMonsterWaveParam{ .level = monster_level };
|
||||||
|
@ -284,6 +315,7 @@ fn addMonsterWaves(allocator: Allocator, battle: *protocol.SceneBattleInfo, mons
|
||||||
try battle.monster_wave_list.append(monster_wave);
|
try battle.monster_wave_list.append(monster_wave);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addStageBlessings(allocator: Allocator, battle: *protocol.SceneBattleInfo, blessings: []const u32) !void {
|
fn addStageBlessings(allocator: Allocator, battle: *protocol.SceneBattleInfo, blessings: []const u32) !void {
|
||||||
for (blessings) |blessing| {
|
for (blessings) |blessing| {
|
||||||
var targetIndexList = ArrayList(u32).init(allocator);
|
var targetIndexList = ArrayList(u32).init(allocator);
|
||||||
|
@ -300,11 +332,10 @@ fn addStageBlessings(allocator: Allocator, battle: *protocol.SceneBattleInfo, bl
|
||||||
try battle.buff_list.append(buff);
|
try battle.buff_list.append(buff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addBattleTargets(allocator: Allocator, battle: *protocol.SceneBattleInfo) !void {
|
fn addBattleTargets(allocator: Allocator, battle: *protocol.SceneBattleInfo) !void {
|
||||||
// PF/AS scoring
|
|
||||||
battle.battle_target_info = ArrayList(protocol.SceneBattleInfo.BattleTargetInfoEntry).init(allocator);
|
battle.battle_target_info = ArrayList(protocol.SceneBattleInfo.BattleTargetInfoEntry).init(allocator);
|
||||||
|
|
||||||
// target hardcode
|
|
||||||
var pfTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) };
|
var pfTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) };
|
||||||
if (ChallengeData.on_challenge == true) {
|
if (ChallengeData.on_challenge == true) {
|
||||||
if (NodeCheck.challenge_node == 0) {
|
if (NodeCheck.challenge_node == 0) {
|
||||||
|
@ -315,29 +346,57 @@ fn addBattleTargets(allocator: Allocator, battle: *protocol.SceneBattleInfo) !vo
|
||||||
} else {
|
} else {
|
||||||
try pfTargetHead.battle_target_list.append(.{ .id = 10002, .progress = 0, .total_progress = 80000 });
|
try pfTargetHead.battle_target_list.append(.{ .id = 10002, .progress = 0, .total_progress = 80000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
var pfTargetTail = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) };
|
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 = 2001, .progress = 0, .total_progress = 0 });
|
||||||
try pfTargetTail.battle_target_list.append(.{ .id = 2002, .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) };
|
var asTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) };
|
||||||
try asTargetHead.battle_target_list.append(.{ .id = 90005, .progress = 2000, .total_progress = 0 });
|
try asTargetHead.battle_target_list.append(.{ .id = 90005, .progress = 2000, .total_progress = 0 });
|
||||||
|
|
||||||
switch (battle.stage_id) {
|
switch (battle.stage_id) {
|
||||||
// PF
|
30019000...30019100, 30021000...30021100, 30301000...30319000 => { // PF
|
||||||
30019000...30019100, 30021000...30021100, 30301000...30319000 => {
|
|
||||||
try battle.battle_target_info.append(.{ .key = 1, .value = pfTargetHead });
|
try battle.battle_target_info.append(.{ .key = 1, .value = pfTargetHead });
|
||||||
// fill blank target
|
|
||||||
for (2..4) |i| {
|
for (2..4) |i| {
|
||||||
try battle.battle_target_info.append(.{ .key = @intCast(i) });
|
try battle.battle_target_info.append(.{ .key = @intCast(i), .value = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) } });
|
||||||
}
|
}
|
||||||
try battle.battle_target_info.append(.{ .key = 5, .value = pfTargetTail });
|
try battle.battle_target_info.append(.{ .key = 5, .value = pfTargetTail });
|
||||||
},
|
},
|
||||||
// AS
|
420100...420900 => { // AS
|
||||||
420100...420900 => {
|
|
||||||
try battle.battle_target_info.append(.{ .key = 1, .value = asTargetHead });
|
try battle.battle_target_info.append(.{ .key = 1, .value = asTargetHead });
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn commonBattleSetup(
|
||||||
|
allocator: Allocator,
|
||||||
|
battle: *protocol.SceneBattleInfo,
|
||||||
|
selected_avatar_ids: []const u32,
|
||||||
|
avatar_configs: []const Config.Avatar,
|
||||||
|
monster_wave_configs: std.ArrayList(std.ArrayList(u32)),
|
||||||
|
monster_level: u32,
|
||||||
|
stage_blessings: []const u32,
|
||||||
|
) !void {
|
||||||
|
var avatarIndex: u32 = 0;
|
||||||
|
for (selected_avatar_ids) |selected_id| {
|
||||||
|
for (avatar_configs) |avatarConf| {
|
||||||
|
if (avatarConf.id == selected_id) {
|
||||||
|
const avatar = try createBattleAvatar(allocator, avatarConf);
|
||||||
|
try addTechniqueBuffs(allocator, battle, avatar, avatarConf, avatarIndex);
|
||||||
|
try battle.battle_avatar_list.append(avatar);
|
||||||
|
avatarIndex += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (avatarIndex >= 4) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try addMonsterWaves(allocator, battle, monster_wave_configs, monster_level);
|
||||||
|
try addTriggerAttack(allocator, battle);
|
||||||
|
try addStageBlessings(allocator, battle, stage_blessings);
|
||||||
|
try addGolbalPassive(allocator, battle);
|
||||||
|
try addBattleTargets(allocator, battle);
|
||||||
|
}
|
||||||
pub const BattleManager = struct {
|
pub const BattleManager = struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
|
@ -347,83 +406,75 @@ pub const BattleManager = struct {
|
||||||
|
|
||||||
pub fn createBattle(self: *BattleManager) !protocol.SceneBattleInfo {
|
pub fn createBattle(self: *BattleManager) !protocol.SceneBattleInfo {
|
||||||
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
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 battle = createBattleInfo(
|
||||||
var initial_mode = false;
|
self.allocator,
|
||||||
while (true) {
|
config,
|
||||||
if (!initial_mode) {
|
@intCast(config.battle_config.monster_wave.items.len),
|
||||||
for (selectedAvatarID) |selected_id| {
|
config.battle_config.stage_id,
|
||||||
for (config.avatar_config.items) |avatarConf| {
|
config.battle_config.cycle_count,
|
||||||
if (avatarConf.id == selected_id) {
|
);
|
||||||
const avatar = try createBattleAvatar(self.allocator, avatarConf);
|
|
||||||
try addTechniqueBuffs(self.allocator, &battle, avatar, avatarConf, avatarIndex);
|
try commonBattleSetup(
|
||||||
try battle.battle_avatar_list.append(avatar);
|
self.allocator,
|
||||||
avatarIndex += 1;
|
&battle,
|
||||||
break;
|
&selectedAvatarID,
|
||||||
}
|
config.avatar_config.items,
|
||||||
}
|
config.battle_config.monster_wave,
|
||||||
if (avatarIndex >= 4) break;
|
config.battle_config.monster_level,
|
||||||
}
|
config.battle_config.blessings.items,
|
||||||
}
|
);
|
||||||
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 addTriggerAttack(self.allocator, &battle);
|
|
||||||
try addStageBlessings(self.allocator, &battle, config.battle_config.blessings.items);
|
|
||||||
try addGolbalPassive(self.allocator, &battle);
|
|
||||||
try addBattleTargets(self.allocator, &battle);
|
|
||||||
return battle;
|
return battle;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ChallegeStageManager = struct {
|
pub const ChallegeStageManager = struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
game_config_cache: *scene_managers.GameConfigCache,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator) ChallegeStageManager {
|
pub fn init(allocator: Allocator, cache: *scene_managers.GameConfigCache) ChallegeStageManager {
|
||||||
return ChallegeStageManager{ .allocator = allocator };
|
return ChallegeStageManager{
|
||||||
|
.allocator = allocator,
|
||||||
|
.game_config_cache = cache,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createChallegeStage(self: *ChallegeStageManager) !protocol.SceneBattleInfo {
|
pub fn createChallegeStage(self: *ChallegeStageManager) !protocol.SceneBattleInfo {
|
||||||
|
if (ChallengeData.challenge_stageID == 0) {
|
||||||
|
std.log.info("Challenge stage ID is 0, skipping challenge battle creation and returning an empty battle info.", .{});
|
||||||
|
return protocol.SceneBattleInfo.init(self.allocator);
|
||||||
|
}
|
||||||
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
||||||
const stage = try Config.loadStageConfig(self.allocator, "resources/StageConfig.json");
|
const stage_config = &self.game_config_cache.stage_config;
|
||||||
var battle: protocol.SceneBattleInfo = undefined;
|
var battle: protocol.SceneBattleInfo = undefined;
|
||||||
for (stage.stage_config.items) |stageConf| {
|
var found_stage = false;
|
||||||
|
|
||||||
|
for (stage_config.stage_config.items) |stageConf| {
|
||||||
if (stageConf.stage_id == ChallengeData.challenge_stageID) {
|
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);
|
battle = createBattleInfo(
|
||||||
var avatarIndex: u32 = 0;
|
self.allocator,
|
||||||
var initial_mode = false;
|
config,
|
||||||
while (true) {
|
@intCast(stageConf.monster_list.items.len),
|
||||||
if (!initial_mode) {
|
stageConf.stage_id,
|
||||||
for (selectedAvatarID) |selected_id| {
|
if (ChallengeData.challenge_mode != 1) 30 else 4,
|
||||||
for (config.avatar_config.items) |avatarConf| {
|
);
|
||||||
if (avatarConf.id == selected_id) {
|
found_stage = true;
|
||||||
const avatar = try createBattleAvatar(self.allocator, avatarConf);
|
try commonBattleSetup(
|
||||||
try addTechniqueBuffs(self.allocator, &battle, avatar, avatarConf, avatarIndex);
|
self.allocator,
|
||||||
try battle.battle_avatar_list.append(avatar);
|
&battle,
|
||||||
avatarIndex += 1;
|
&selectedAvatarID,
|
||||||
|
config.avatar_config.items,
|
||||||
|
stageConf.monster_list,
|
||||||
|
stageConf.level,
|
||||||
|
ChallengeData.challenge_blessing,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (avatarIndex >= 4) break;
|
if (!found_stage) {
|
||||||
}
|
std.log.err("Challenge stage with ID {d} not found in config.", .{ChallengeData.challenge_stageID});
|
||||||
}
|
return error.StageNotFound;
|
||||||
if (avatarIndex == 0 and !initial_mode) {
|
|
||||||
initial_mode = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
try addMonsterWaves(self.allocator, &battle, stageConf.monster_list, stageConf.level);
|
|
||||||
try addTriggerAttack(self.allocator, &battle);
|
|
||||||
try addStageBlessings(self.allocator, &battle, ChallengeData.challenge_blessing);
|
|
||||||
try addGolbalPassive(self.allocator, &battle);
|
|
||||||
try addBattleTargets(self.allocator, &battle);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return battle;
|
return battle;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +1,51 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const protocol = @import("protocol");
|
const protocol = @import("protocol");
|
||||||
const Session = @import("../Session.zig");
|
const Session = @import("../session.zig");
|
||||||
const Packet = @import("../Packet.zig");
|
const Packet = @import("../Packet.zig");
|
||||||
const Config = @import("../services/config.zig");
|
const Config = @import("../services/config.zig");
|
||||||
const Res_config = @import("../services/res_config.zig");
|
const Res_config = @import("../services/res_config.zig");
|
||||||
const Data = @import("../data.zig");
|
const Item = @import("../services/item.zig");
|
||||||
|
const UidGenerator = Item.UidGenerator();
|
||||||
const UidGenerator = @import("../services/item.zig").UidGenerator;
|
|
||||||
const ArrayList = std.ArrayList;
|
const ArrayList = std.ArrayList;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const CmdID = protocol.CmdID;
|
|
||||||
|
|
||||||
pub const SceneManager = struct {
|
pub const SceneManager = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) SceneManager {
|
pub fn init(allocator: Allocator) SceneManager {
|
||||||
return SceneManager{ .allocator = allocator };
|
return SceneManager{ .allocator = allocator };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createScene(
|
fn addAvatarEntities(
|
||||||
self: *SceneManager,
|
scene_group: *protocol.SceneEntityGroupInfo,
|
||||||
plane_id: u32,
|
avatar_configs: []const Config.Avatar,
|
||||||
floor_id: u32,
|
tele_pos: protocol.Vector,
|
||||||
entry_id: u32,
|
tele_rot: protocol.Vector,
|
||||||
teleport_id: u32,
|
uid: u32,
|
||||||
) !protocol.SceneInfo {
|
) !void {
|
||||||
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
for (avatar_configs) |avatarConf| {
|
||||||
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;
|
|
||||||
scene_info.leader_entity_id = config.avatar_config.items[0].id + 100000;
|
|
||||||
scene_info.world_id = 501;
|
|
||||||
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) {
|
|
||||||
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
|
||||||
scene_group.state = 1;
|
|
||||||
for (config.avatar_config.items) |avatarConf| {
|
|
||||||
try scene_group.entity_list.append(.{
|
try scene_group.entity_list.append(.{
|
||||||
.inst_id = 1,
|
.inst_id = 1,
|
||||||
.entity_id = @intCast(avatarConf.id + 100000),
|
.entity_id = @intCast(avatarConf.id + 100000),
|
||||||
.entity = .{ .actor = .{
|
.entity = .{ .actor = .{
|
||||||
.base_avatar_id = avatarConf.id,
|
.base_avatar_id = avatarConf.id,
|
||||||
.avatar_type = .AVATAR_FORMAL_TYPE,
|
.avatar_type = .AVATAR_FORMAL_TYPE,
|
||||||
.uid = 0,
|
.uid = uid,
|
||||||
.map_layer = 0,
|
.map_layer = 0,
|
||||||
} },
|
} },
|
||||||
.motion = .{
|
.motion = .{ .pos = tele_pos, .rot = tele_rot },
|
||||||
.pos = .{ .x = teleConf.pos.x, .y = teleConf.pos.y, .z = teleConf.pos.z },
|
|
||||||
.rot = .{ .x = teleConf.rot.x, .y = teleConf.rot.y, .z = teleConf.rot.z },
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try scene_info.entity_group_list.append(scene_group);
|
|
||||||
}
|
}
|
||||||
}
|
fn addPropEntities(
|
||||||
if (scene_info.plane_id != 10000 and scene_info.plane_id != 10202 and sceneConf.planeID == scene_info.plane_id) {
|
allocator: Allocator,
|
||||||
for (sceneConf.props.items) |propConf| {
|
group_map: *std.AutoHashMap(u32, protocol.SceneEntityGroupInfo),
|
||||||
var scene_group = try getOrCreateGroup(&group_map, propConf.groupId, self.allocator);
|
prop_configs: []const Res_config.Props,
|
||||||
var prop_info = protocol.ScenePropInfo.init(self.allocator);
|
generator: *UidGenerator,
|
||||||
|
) !void {
|
||||||
|
for (prop_configs) |propConf| {
|
||||||
|
var scene_group = try getOrCreateGroup(group_map, propConf.groupId, allocator);
|
||||||
|
var prop_info = protocol.ScenePropInfo.init(allocator);
|
||||||
prop_info.prop_id = propConf.propId;
|
prop_info.prop_id = propConf.propId;
|
||||||
prop_info.prop_state = propConf.propState;
|
prop_info.prop_state = propConf.propState;
|
||||||
try scene_group.entity_list.append(.{
|
try scene_group.entity_list.append(.{
|
||||||
|
@ -80,9 +59,16 @@ pub const SceneManager = struct {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (sceneConf.monsters.items) |monsConf| {
|
}
|
||||||
var scene_group = try getOrCreateGroup(&group_map, monsConf.groupId, self.allocator);
|
fn addMonsterEntities(
|
||||||
var monster_info = protocol.SceneNpcMonsterInfo.init(self.allocator);
|
allocator: Allocator,
|
||||||
|
group_map: *std.AutoHashMap(u32, protocol.SceneEntityGroupInfo),
|
||||||
|
monster_configs: []const Res_config.Monsters,
|
||||||
|
generator: *UidGenerator,
|
||||||
|
) !void {
|
||||||
|
for (monster_configs) |monsConf| {
|
||||||
|
var scene_group = try getOrCreateGroup(group_map, monsConf.groupId, allocator);
|
||||||
|
var monster_info = protocol.SceneNpcMonsterInfo.init(allocator);
|
||||||
monster_info.monster_id = monsConf.monsterId;
|
monster_info.monster_id = monsConf.monsterId;
|
||||||
monster_info.event_id = monsConf.eventId;
|
monster_info.event_id = monsConf.eventId;
|
||||||
monster_info.world_level = 6;
|
monster_info.world_level = 6;
|
||||||
|
@ -98,7 +84,56 @@ pub const SceneManager = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn createScene(
|
||||||
|
self: *SceneManager,
|
||||||
|
plane_id: u32,
|
||||||
|
floor_id: u32,
|
||||||
|
entry_id: u32,
|
||||||
|
teleport_id: u32,
|
||||||
|
) !protocol.SceneInfo {
|
||||||
|
const config = try Config.loadGameConfig(self.allocator, "config.json");
|
||||||
|
const res_config = global_game_config_cache.res_config;
|
||||||
|
var generator = Item.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;
|
||||||
|
scene_info.leader_entity_id = config.avatar_config.items[0].id + 100000;
|
||||||
|
scene_info.world_id = 501;
|
||||||
|
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) {
|
||||||
|
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
||||||
|
scene_group.state = 1;
|
||||||
|
const proto_tele_pos = protocol.Vector{
|
||||||
|
.x = teleConf.pos.x,
|
||||||
|
.y = teleConf.pos.y,
|
||||||
|
.z = teleConf.pos.z,
|
||||||
|
};
|
||||||
|
const proto_tele_rot = protocol.Vector{
|
||||||
|
.x = teleConf.rot.x,
|
||||||
|
.y = teleConf.rot.y,
|
||||||
|
.z = teleConf.rot.z,
|
||||||
|
};
|
||||||
|
|
||||||
|
try addAvatarEntities(&scene_group, config.avatar_config.items, proto_tele_pos, proto_tele_rot, 0);
|
||||||
|
try scene_info.entity_group_list.append(scene_group);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (scene_info.plane_id != 10000 and scene_info.plane_id != 10202 and sceneConf.planeID == scene_info.plane_id) {
|
||||||
|
try addPropEntities(self.allocator, &group_map, sceneConf.props.items, &generator);
|
||||||
|
try addMonsterEntities(self.allocator, &group_map, sceneConf.monsters.items, &generator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var iter = group_map.iterator();
|
var iter = group_map.iterator();
|
||||||
while (iter.next()) |entry| {
|
while (iter.next()) |entry| {
|
||||||
try scene_info.entity_group_list.append(entry.value_ptr.*);
|
try scene_info.entity_group_list.append(entry.value_ptr.*);
|
||||||
|
@ -126,7 +161,7 @@ pub const SceneManager = struct {
|
||||||
}
|
}
|
||||||
return scene_info;
|
return scene_info;
|
||||||
}
|
}
|
||||||
fn getOrCreateGroup(group_map: *std.AutoHashMap(u32, protocol.SceneEntityGroupInfo), group_id: u32, allocator: std.mem.Allocator) !*protocol.SceneEntityGroupInfo {
|
fn getOrCreateGroup(group_map: *std.AutoHashMap(u32, protocol.SceneEntityGroupInfo), group_id: u32, allocator: Allocator) !*protocol.SceneEntityGroupInfo {
|
||||||
if (group_map.getPtr(group_id)) |existing_group| {
|
if (group_map.getPtr(group_id)) |existing_group| {
|
||||||
return existing_group;
|
return existing_group;
|
||||||
}
|
}
|
||||||
|
@ -137,9 +172,11 @@ pub const SceneManager = struct {
|
||||||
return group_map.getPtr(group_id).?;
|
return group_map.getPtr(group_id).?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ChallengeSceneManager = struct {
|
pub const ChallengeSceneManager = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
pub fn init(allocator: std.mem.Allocator) ChallengeSceneManager {
|
|
||||||
|
pub fn init(allocator: Allocator) ChallengeSceneManager {
|
||||||
return ChallengeSceneManager{ .allocator = allocator };
|
return ChallengeSceneManager{ .allocator = allocator };
|
||||||
}
|
}
|
||||||
pub fn createScene(
|
pub fn createScene(
|
||||||
|
@ -154,8 +191,8 @@ pub const ChallengeSceneManager = struct {
|
||||||
group_id: u32,
|
group_id: u32,
|
||||||
maze_group_id: u32,
|
maze_group_id: u32,
|
||||||
) !protocol.SceneInfo {
|
) !protocol.SceneInfo {
|
||||||
const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json");
|
const res_config = global_game_config_cache.res_config;
|
||||||
var generator = UidGenerator().init();
|
var generator = Item.UidGenerator().init();
|
||||||
|
|
||||||
var scene_info = protocol.SceneInfo.init(self.allocator);
|
var scene_info = protocol.SceneInfo.init(self.allocator);
|
||||||
scene_info.game_mode_type = 4;
|
scene_info.game_mode_type = 4;
|
||||||
|
@ -168,17 +205,17 @@ pub const ChallengeSceneManager = struct {
|
||||||
.group_id = maze_group_id,
|
.group_id = maze_group_id,
|
||||||
.is_default = true,
|
.is_default = true,
|
||||||
});
|
});
|
||||||
{ // Character
|
{ // Character Group
|
||||||
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
||||||
scene_group.state = 1;
|
scene_group.state = 1;
|
||||||
scene_group.group_id = 0;
|
scene_group.group_id = 0;
|
||||||
for (avatar_list.items) |avatarConf| {
|
for (avatar_list.items) |avatar_base_id| {
|
||||||
try scene_group.entity_list.append(.{
|
try scene_group.entity_list.append(.{
|
||||||
.inst_id = 1,
|
.inst_id = 1,
|
||||||
.entity_id = @intCast(avatarConf + 100000),
|
.entity_id = @intCast(avatar_base_id + 100000),
|
||||||
.entity = .{
|
.entity = .{
|
||||||
.actor = .{
|
.actor = .{
|
||||||
.base_avatar_id = avatarConf,
|
.base_avatar_id = avatar_base_id,
|
||||||
.avatar_type = .AVATAR_FORMAL_TYPE,
|
.avatar_type = .AVATAR_FORMAL_TYPE,
|
||||||
.uid = 1,
|
.uid = 1,
|
||||||
.map_layer = 0,
|
.map_layer = 0,
|
||||||
|
@ -191,10 +228,10 @@ pub const ChallengeSceneManager = struct {
|
||||||
}
|
}
|
||||||
for (res_config.scene_config.items) |sceneConf| {
|
for (res_config.scene_config.items) |sceneConf| {
|
||||||
if (sceneConf.planeID == scene_info.plane_id) {
|
if (sceneConf.planeID == scene_info.plane_id) {
|
||||||
for (sceneConf.monsters.items) |monsConf| { //create monster
|
for (sceneConf.monsters.items) |monsConf| {
|
||||||
|
if (monsConf.groupId == group_id) {
|
||||||
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
||||||
scene_group.state = 1;
|
scene_group.state = 1;
|
||||||
if (monsConf.groupId == group_id) {
|
|
||||||
scene_group.group_id = group_id;
|
scene_group.group_id = group_id;
|
||||||
|
|
||||||
var monster_info = protocol.SceneNpcMonsterInfo.init(self.allocator);
|
var monster_info = protocol.SceneNpcMonsterInfo.init(self.allocator);
|
||||||
|
@ -213,13 +250,10 @@ pub const ChallengeSceneManager = struct {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
try scene_info.entity_group_list.append(scene_group);
|
try scene_info.entity_group_list.append(scene_group);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
for (sceneConf.props.items) |propConf| {
|
||||||
}
|
|
||||||
for (res_config.scene_config.items) |sceneConf| {
|
|
||||||
if (sceneConf.planeID == scene_info.plane_id) {
|
|
||||||
for (sceneConf.props.items) |propConf| { //create props
|
|
||||||
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
|
||||||
scene_group.state = 1;
|
scene_group.state = 1;
|
||||||
scene_group.group_id = group_id;
|
scene_group.group_id = group_id;
|
||||||
|
@ -232,7 +266,7 @@ pub const ChallengeSceneManager = struct {
|
||||||
.entity = .{ .prop = prop_info },
|
.entity = .{ .prop = prop_info },
|
||||||
.group_id = group_id,
|
.group_id = group_id,
|
||||||
.inst_id = propConf.instId,
|
.inst_id = propConf.instId,
|
||||||
.entity_id = 0,
|
.entity_id = generator.nextId(),
|
||||||
.motion = .{
|
.motion = .{
|
||||||
.pos = .{ .x = propConf.pos.x, .y = propConf.pos.y, .z = propConf.pos.z },
|
.pos = .{ .x = propConf.pos.x, .y = propConf.pos.y, .z = propConf.pos.z },
|
||||||
.rot = .{ .x = propConf.rot.x, .y = propConf.rot.y, .z = propConf.rot.z },
|
.rot = .{ .x = propConf.rot.x, .y = propConf.rot.y, .z = propConf.rot.z },
|
||||||
|
@ -245,10 +279,9 @@ pub const ChallengeSceneManager = struct {
|
||||||
return scene_info;
|
return scene_info;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MazeMapManager = struct {
|
pub const MazeMapManager = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
pub fn init(allocator: std.mem.Allocator) MazeMapManager {
|
pub fn init(allocator: Allocator) MazeMapManager {
|
||||||
return MazeMapManager{ .allocator = allocator };
|
return MazeMapManager{ .allocator = allocator };
|
||||||
}
|
}
|
||||||
pub fn setMazeMapData(
|
pub fn setMazeMapData(
|
||||||
|
@ -256,21 +289,22 @@ pub const MazeMapManager = struct {
|
||||||
map_info: *protocol.SceneMapInfo,
|
map_info: *protocol.SceneMapInfo,
|
||||||
floor_id: u32,
|
floor_id: u32,
|
||||||
) !void {
|
) !void {
|
||||||
const config = try Config.loadMapEntranceConfig(self.allocator, "resources/MapEntrance.json");
|
const map_entrance_config = global_game_config_cache.map_entrance_config;
|
||||||
const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json");
|
|
||||||
|
const res_config = global_game_config_cache.res_config;
|
||||||
var plane_ids = ArrayList(u32).init(self.allocator);
|
var plane_ids = ArrayList(u32).init(self.allocator);
|
||||||
for (config.map_entrance_config.items) |entrConf| {
|
defer plane_ids.deinit();
|
||||||
|
for (map_entrance_config.map_entrance_config.items) |entrConf| {
|
||||||
if (entrConf.floor_id == floor_id) {
|
if (entrConf.floor_id == floor_id) {
|
||||||
try plane_ids.append(entrConf.plane_id);
|
try plane_ids.append(entrConf.plane_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
map_info.maze_group_list = ArrayList(protocol.MazeGroup).init(self.allocator);
|
map_info.maze_group_list = ArrayList(protocol.MazeGroup).init(self.allocator);
|
||||||
map_info.maze_prop_list = ArrayList(protocol.MazePropState).init(self.allocator);
|
map_info.maze_prop_list = ArrayList(protocol.MazePropState).init(self.allocator);
|
||||||
for (res_config.scene_config.items) |resConf| {
|
for (res_config.scene_config.items) |sceneConf| {
|
||||||
for (plane_ids.items) |plane_id| {
|
for (plane_ids.items) |plane_id| {
|
||||||
if (resConf.planeID == plane_id) {
|
if (sceneConf.planeID == plane_id) {
|
||||||
for (resConf.props.items) |propConf| {
|
for (sceneConf.props.items) |propConf| {
|
||||||
try map_info.maze_group_list.append(protocol.MazeGroup{
|
try map_info.maze_group_list.append(protocol.MazeGroup{
|
||||||
.NOBKEONAKLE = ArrayList(u32).init(self.allocator),
|
.NOBKEONAKLE = ArrayList(u32).init(self.allocator),
|
||||||
.group_id = propConf.groupId,
|
.group_id = propConf.groupId,
|
||||||
|
@ -286,3 +320,35 @@ pub const MazeMapManager = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
pub const GameConfigCache = struct {
|
||||||
|
allocator: Allocator,
|
||||||
|
res_config: Res_config.SceneConfig,
|
||||||
|
map_entrance_config: Config.MapEntranceConfig,
|
||||||
|
stage_config: Config.StageConfig,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator) !GameConfigCache {
|
||||||
|
const res_cfg = try Res_config.anchorLoader(allocator, "resources/res.json");
|
||||||
|
const map_entr_cfg = try Config.loadMapEntranceConfig(allocator, "resources/MapEntrance.json");
|
||||||
|
const stage_cfg = try Config.loadStageConfig(allocator, "resources/StageConfig.json");
|
||||||
|
|
||||||
|
return GameConfigCache{
|
||||||
|
.allocator = allocator,
|
||||||
|
.res_config = res_cfg,
|
||||||
|
.map_entrance_config = map_entr_cfg,
|
||||||
|
.stage_config = stage_cfg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn deinit(_: *GameConfigCache) void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub var global_game_config_cache: GameConfigCache = undefined;
|
||||||
|
pub var global_main_allocator: Allocator = undefined;
|
||||||
|
|
||||||
|
pub fn initGameGlobals(main_allocator: Allocator) !void {
|
||||||
|
global_main_allocator = main_allocator;
|
||||||
|
global_game_config_cache = try GameConfigCache.init(main_allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinitGameGlobals() void {
|
||||||
|
global_game_config_cache.deinit();
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Session = @import("Session.zig");
|
const Session = @import("Session.zig");
|
||||||
|
const Cache = @import("../src/manager/scene_mgr.zig");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub fn listen() !void {
|
pub fn listen() !void {
|
||||||
|
@ -16,21 +16,31 @@ pub fn listen() !void {
|
||||||
const conn = listener.accept() catch continue;
|
const conn = listener.accept() catch continue;
|
||||||
errdefer conn.stream.close();
|
errdefer conn.stream.close();
|
||||||
|
|
||||||
const thread = try std.Thread.spawn(.{}, runSession, .{ conn.address, conn.stream });
|
const thread = try std.Thread.spawn(.{}, runSession, .{
|
||||||
|
conn.address,
|
||||||
|
conn.stream,
|
||||||
|
Cache.global_main_allocator,
|
||||||
|
&Cache.global_game_config_cache,
|
||||||
|
});
|
||||||
thread.detach();
|
thread.detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runSession(address: std.net.Address, stream: std.net.Stream) !void {
|
fn runSession(
|
||||||
|
address: std.net.Address,
|
||||||
|
stream: std.net.Stream,
|
||||||
|
main_allocator: Allocator,
|
||||||
|
game_config_cache: *Cache.GameConfigCache,
|
||||||
|
) !void {
|
||||||
std.log.info("new connection from {}", .{address});
|
std.log.info("new connection from {}", .{address});
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer if (gpa.deinit() == .leak) std.log.err("memory leaks were detected for session at {}", .{address});
|
defer if (gpa.deinit() == .leak) std.log.err("memory leaks were detected for session at {}", .{address});
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
const session_allocator = gpa.allocator();
|
||||||
|
|
||||||
const session = try allocator.create(Session);
|
const session = try session_allocator.create(Session);
|
||||||
session.* = Session.init(address, stream, allocator);
|
session.* = Session.init(address, stream, session_allocator, main_allocator, game_config_cache);
|
||||||
|
|
||||||
if (session.*.run()) |_| {
|
if (session.*.run()) |_| {
|
||||||
std.log.info("client from {} disconnected", .{address});
|
std.log.info("client from {} disconnected", .{address});
|
||||||
|
@ -38,5 +48,5 @@ fn runSession(address: std.net.Address, stream: std.net.Stream) !void {
|
||||||
std.log.err("session disconnected with an error: {}", .{err});
|
std.log.err("session disconnected with an error: {}", .{err});
|
||||||
}
|
}
|
||||||
|
|
||||||
allocator.destroy(session);
|
session_allocator.destroy(session);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ const ChallengeData = @import("challenge.zig");
|
||||||
const BattleManager = @import("../manager/battle_mgr.zig").BattleManager;
|
const BattleManager = @import("../manager/battle_mgr.zig").BattleManager;
|
||||||
const ChallegeStageManager = @import("../manager/battle_mgr.zig").ChallegeStageManager;
|
const ChallegeStageManager = @import("../manager/battle_mgr.zig").ChallegeStageManager;
|
||||||
const NodeCheck = @import("../commands/value.zig");
|
const NodeCheck = @import("../commands/value.zig");
|
||||||
|
const scene_managers = @import("../manager/scene_mgr.zig");
|
||||||
|
|
||||||
const ArrayList = std.ArrayList;
|
const ArrayList = std.ArrayList;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
@ -78,7 +79,7 @@ pub fn onStartBattleCollege(session: *Session, packet: *const Packet, allocator:
|
||||||
pub fn onSceneCastSkill(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
pub fn onSceneCastSkill(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
var battle_mgr = BattleManager.init(allocator);
|
var battle_mgr = BattleManager.init(allocator);
|
||||||
const battle = try battle_mgr.createBattle();
|
const battle = try battle_mgr.createBattle();
|
||||||
var challege_mgr = ChallegeStageManager.init(allocator);
|
var challege_mgr = ChallegeStageManager.init(allocator, &scene_managers.global_game_config_cache);
|
||||||
const challenge = try challege_mgr.createChallegeStage();
|
const challenge = try challege_mgr.createChallegeStage();
|
||||||
const req = try packet.getProto(protocol.SceneCastSkillCsReq, allocator);
|
const req = try packet.getProto(protocol.SceneCastSkillCsReq, allocator);
|
||||||
var battle_info: ?protocol.SceneBattleInfo = null;
|
var battle_info: ?protocol.SceneBattleInfo = null;
|
||||||
|
@ -126,7 +127,7 @@ pub fn onSceneCastSkill(session: *Session, packet: *const Packet, allocator: All
|
||||||
pub fn onGetCurBattleInfo(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
pub fn onGetCurBattleInfo(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
var battle_mgr = BattleManager.init(allocator);
|
var battle_mgr = BattleManager.init(allocator);
|
||||||
const battle = try battle_mgr.createBattle();
|
const battle = try battle_mgr.createBattle();
|
||||||
var challege_mgr = ChallegeStageManager.init(allocator);
|
var challege_mgr = ChallegeStageManager.init(allocator, &scene_managers.global_game_config_cache);
|
||||||
const challenge = try challege_mgr.createChallegeStage();
|
const challenge = try challege_mgr.createChallegeStage();
|
||||||
|
|
||||||
var rsp = protocol.GetCurBattleInfoScRsp.init(allocator);
|
var rsp = protocol.GetCurBattleInfoScRsp.init(allocator);
|
||||||
|
|
|
@ -11,7 +11,7 @@ const SceneManager = @import("../manager/scene_mgr.zig").SceneManager;
|
||||||
const ChallengeSceneManager = @import("../manager/scene_mgr.zig").ChallengeSceneManager;
|
const ChallengeSceneManager = @import("../manager/scene_mgr.zig").ChallengeSceneManager;
|
||||||
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
const ChallengeLineupManager = @import("../manager/lineup_mgr.zig").ChallengeLineupManager;
|
const ChallengeLineupManager = @import("../manager/lineup_mgr.zig").ChallengeLineupManager;
|
||||||
const NodeCheck = @import("../commands/value.zig");
|
const Value = @import("../commands/value.zig");
|
||||||
|
|
||||||
const ArrayList = std.ArrayList;
|
const ArrayList = std.ArrayList;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
@ -159,10 +159,23 @@ pub fn onGetCurChallengeScRsp(session: *Session, _: *const Packet, allocator: Al
|
||||||
pub fn onStartChallenge(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
pub fn onStartChallenge(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
const req = try packet.getProto(protocol.StartChallengeCsReq, allocator);
|
const req = try packet.getProto(protocol.StartChallengeCsReq, allocator);
|
||||||
var rsp = protocol.StartChallengeScRsp.init(allocator);
|
var rsp = protocol.StartChallengeScRsp.init(allocator);
|
||||||
|
if (Value.custom_mode == true) {
|
||||||
|
challengeID = Value.selected_challenge_id;
|
||||||
|
challenge_buffID = Value.selected_buff_id;
|
||||||
|
if (Value.challenge_node == 0) {
|
||||||
|
for (req.first_lineup.items) |id| {
|
||||||
|
try avatar_list.append(id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
{
|
||||||
|
for (req.second_lineup.items) |id| {
|
||||||
|
try avatar_list.append(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
challengeID = req.challenge_id;
|
challengeID = req.challenge_id;
|
||||||
|
if (Value.challenge_node == 0) {
|
||||||
if (NodeCheck.challenge_node == 0) {
|
|
||||||
for (req.first_lineup.items) |id| {
|
for (req.first_lineup.items) |id| {
|
||||||
try avatar_list.append(id);
|
try avatar_list.append(id);
|
||||||
}
|
}
|
||||||
|
@ -179,6 +192,7 @@ pub fn onStartChallenge(session: *Session, packet: *const Packet, allocator: All
|
||||||
if (challengeID > 30000)
|
if (challengeID > 30000)
|
||||||
challenge_buffID = req.stage_info.?.KFELKJLDKEH.?.boss_info.buff_two;
|
challenge_buffID = req.stage_info.?.KFELKJLDKEH.?.boss_info.buff_two;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var lineup_manager = ChallengeLineupManager.init(allocator);
|
var lineup_manager = ChallengeLineupManager.init(allocator);
|
||||||
const lineup_info = try lineup_manager.createLineup(avatar_list);
|
const lineup_info = try lineup_manager.createLineup(avatar_list);
|
||||||
|
|
||||||
|
|
|
@ -74,8 +74,10 @@ const Activity = struct {
|
||||||
activity_module_list: ArrayList(u32),
|
activity_module_list: ArrayList(u32),
|
||||||
panel_id: u32,
|
panel_id: u32,
|
||||||
};
|
};
|
||||||
const ChallengeConfig = struct {
|
pub const ChallengeConfig = struct {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
group_id: u32,
|
||||||
|
floor: ?u32,
|
||||||
npc_monster_id_list1: ArrayList(u32),
|
npc_monster_id_list1: ArrayList(u32),
|
||||||
npc_monster_id_list2: ArrayList(u32),
|
npc_monster_id_list2: ArrayList(u32),
|
||||||
event_id_list1: ArrayList(u32),
|
event_id_list1: ArrayList(u32),
|
||||||
|
@ -101,6 +103,15 @@ const MazePlane = struct {
|
||||||
challenge_plane_id: u32,
|
challenge_plane_id: u32,
|
||||||
world_id: u32,
|
world_id: u32,
|
||||||
};
|
};
|
||||||
|
const BuffList = struct {
|
||||||
|
id: u32,
|
||||||
|
name: []const u8,
|
||||||
|
};
|
||||||
|
const TextMap = struct {
|
||||||
|
group_id: u32,
|
||||||
|
buff_list1: ArrayList(BuffList),
|
||||||
|
buff_list2: ArrayList(BuffList),
|
||||||
|
};
|
||||||
pub const GameConfig = struct {
|
pub const GameConfig = struct {
|
||||||
battle_config: BattleConfig,
|
battle_config: BattleConfig,
|
||||||
avatar_config: ArrayList(Avatar),
|
avatar_config: ArrayList(Avatar),
|
||||||
|
@ -132,6 +143,9 @@ pub const MapEntranceConfig = struct {
|
||||||
pub const MazePlaneConfig = struct {
|
pub const MazePlaneConfig = struct {
|
||||||
maze_plane_config: ArrayList(MazePlane),
|
maze_plane_config: ArrayList(MazePlane),
|
||||||
};
|
};
|
||||||
|
pub const TextMapConfig = struct {
|
||||||
|
text_map_config: ArrayList(TextMap),
|
||||||
|
};
|
||||||
const ErrorSet = error{ CommandError, SystemResources, Unexpected, AccessDenied, WouldBlock, ConnectionResetByPeer, OutOfMemory, DiskQuota, FileTooBig, InputOutput, NoSpaceLeft, DeviceBusy, InvalidArgument, BrokenPipe, OperationAborted, NotOpenForWriting, LockViolation, Overflow, InvalidCharacter, ProcessFdQuotaExceeded, SystemFdQuotaExceeded, SymLinkLoop, NameTooLong, FileNotFound, NotDir, NoDevice, SharingViolation, PathAlreadyExists, PipeBusy, InvalidUtf8, InvalidWtf8, BadPathName, NetworkNotFound, AntivirusInterference, IsDir, FileLocksNotSupported, FileBusy, ConnectionTimedOut, NotOpenForReading, SocketNotConnected, Unseekable, UnexpectedToken, InvalidNumber, InvalidEnumTag, DuplicateField, UnknownField, MissingField, LengthMismatch, SyntaxError, UnexpectedEndOfInput, BufferUnderrun, ValueTooLong, InsufficientTokens, InvalidFormat };
|
const ErrorSet = error{ CommandError, SystemResources, Unexpected, AccessDenied, WouldBlock, ConnectionResetByPeer, OutOfMemory, DiskQuota, FileTooBig, InputOutput, NoSpaceLeft, DeviceBusy, InvalidArgument, BrokenPipe, OperationAborted, NotOpenForWriting, LockViolation, Overflow, InvalidCharacter, ProcessFdQuotaExceeded, SystemFdQuotaExceeded, SymLinkLoop, NameTooLong, FileNotFound, NotDir, NoDevice, SharingViolation, PathAlreadyExists, PipeBusy, InvalidUtf8, InvalidWtf8, BadPathName, NetworkNotFound, AntivirusInterference, IsDir, FileLocksNotSupported, FileBusy, ConnectionTimedOut, NotOpenForReading, SocketNotConnected, Unseekable, UnexpectedToken, InvalidNumber, InvalidEnumTag, DuplicateField, UnknownField, MissingField, LengthMismatch, SyntaxError, UnexpectedEndOfInput, BufferUnderrun, ValueTooLong, InsufficientTokens, InvalidFormat };
|
||||||
|
|
||||||
pub fn loadConfig(
|
pub fn loadConfig(
|
||||||
|
@ -195,6 +209,10 @@ pub fn loadMazePlaneConfig(allocator: Allocator, filename: []const u8) ErrorSet!
|
||||||
return loadConfig(MazePlaneConfig, parseMazePlaneConfig, allocator, filename);
|
return loadConfig(MazePlaneConfig, parseMazePlaneConfig, allocator, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn loadTextMapConfig(allocator: Allocator, filename: []const u8) ErrorSet!TextMapConfig {
|
||||||
|
return loadConfig(TextMapConfig, parseTextMapConfig, allocator, filename);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parseConfig(root: std.json.Value, allocator: Allocator) ErrorSet!GameConfig {
|
pub fn parseConfig(root: std.json.Value, allocator: Allocator) ErrorSet!GameConfig {
|
||||||
const battle_config_json = root.object.get("battle_config").?;
|
const battle_config_json = root.object.get("battle_config").?;
|
||||||
var battle_config = BattleConfig{
|
var battle_config = BattleConfig{
|
||||||
|
@ -345,6 +363,8 @@ fn parseChallengeConfig(root: std.json.Value, allocator: Allocator) ErrorSet!Cha
|
||||||
for (root.object.get("challenge_config").?.array.items) |challenge_json| {
|
for (root.object.get("challenge_config").?.array.items) |challenge_json| {
|
||||||
var challenge = ChallengeConfig{
|
var challenge = ChallengeConfig{
|
||||||
.id = @intCast(challenge_json.object.get("ID").?.integer),
|
.id = @intCast(challenge_json.object.get("ID").?.integer),
|
||||||
|
.group_id = @intCast(challenge_json.object.get("GroupID").?.integer),
|
||||||
|
.floor = if (challenge_json.object.get("Floor")) |val| @intCast(val.integer) else null,
|
||||||
.maze_buff_id = @intCast(challenge_json.object.get("MazeBuffID").?.integer),
|
.maze_buff_id = @intCast(challenge_json.object.get("MazeBuffID").?.integer),
|
||||||
.npc_monster_id_list1 = ArrayList(u32).init(allocator),
|
.npc_monster_id_list1 = ArrayList(u32).init(allocator),
|
||||||
.npc_monster_id_list2 = ArrayList(u32).init(allocator),
|
.npc_monster_id_list2 = ArrayList(u32).init(allocator),
|
||||||
|
@ -420,6 +440,39 @@ fn parseMazePlaneConfig(root: std.json.Value, allocator: Allocator) ErrorSet!Maz
|
||||||
.maze_plane_config = maze_plane_config,
|
.maze_plane_config = maze_plane_config,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
fn parseTextMapConfig(root: std.json.Value, allocator: Allocator) !TextMapConfig {
|
||||||
|
var text_map_config = ArrayList(TextMap).init(allocator);
|
||||||
|
|
||||||
|
for (root.object.get("text_map_config").?.array.items) |entry| {
|
||||||
|
var buff_list1 = ArrayList(BuffList).init(allocator);
|
||||||
|
var buff_list2 = ArrayList(BuffList).init(allocator);
|
||||||
|
|
||||||
|
for (entry.object.get("BuffList1").?.array.items) |buff| {
|
||||||
|
try buff_list1.append(BuffList{
|
||||||
|
.id = @intCast(buff.object.get("id").?.integer),
|
||||||
|
.name = buff.object.get("name").?.string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entry.object.get("BuffList2").?.array.items) |buff| {
|
||||||
|
try buff_list2.append(BuffList{
|
||||||
|
.id = @intCast(buff.object.get("id").?.integer),
|
||||||
|
.name = buff.object.get("name").?.string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try text_map_config.append(TextMap{
|
||||||
|
.group_id = @intCast(entry.object.get("GroupID").?.integer),
|
||||||
|
.buff_list1 = buff_list1,
|
||||||
|
.buff_list2 = buff_list2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextMapConfig{
|
||||||
|
.text_map_config = text_map_config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn parseRelic(relic_str: []const u8, allocator: Allocator) !Relic {
|
fn parseRelic(relic_str: []const u8, allocator: Allocator) !Relic {
|
||||||
var tokens = ArrayList([]const u8).init(allocator);
|
var tokens = ArrayList([]const u8).init(allocator);
|
||||||
defer tokens.deinit();
|
defer tokens.deinit();
|
||||||
|
|
|
@ -3,7 +3,7 @@ const ArrayList = std.ArrayList;
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const ResConfig = struct {
|
pub const ResConfig = struct {
|
||||||
planeID: u32,
|
planeID: u32,
|
||||||
props: ArrayList(Props),
|
props: ArrayList(Props),
|
||||||
monsters: ArrayList(Monsters),
|
monsters: ArrayList(Monsters),
|
||||||
|
@ -22,7 +22,7 @@ const Teleports = struct {
|
||||||
rot: Vector,
|
rot: Vector,
|
||||||
teleportId: u32,
|
teleportId: u32,
|
||||||
};
|
};
|
||||||
const Monsters = struct {
|
pub const Monsters = struct {
|
||||||
groupId: u32,
|
groupId: u32,
|
||||||
instId: u32,
|
instId: u32,
|
||||||
eventId: u32,
|
eventId: u32,
|
||||||
|
@ -30,7 +30,7 @@ const Monsters = struct {
|
||||||
rot: Vector,
|
rot: Vector,
|
||||||
monsterId: u32,
|
monsterId: u32,
|
||||||
};
|
};
|
||||||
const Props = struct {
|
pub const Props = struct {
|
||||||
groupId: u32,
|
groupId: u32,
|
||||||
instId: u32,
|
instId: u32,
|
||||||
propState: u32,
|
propState: u32,
|
||||||
|
|
|
@ -7,6 +7,7 @@ const Data = @import("../data.zig");
|
||||||
const Res_config = @import("res_config.zig");
|
const Res_config = @import("res_config.zig");
|
||||||
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
const LineupManager = @import("../manager/lineup_mgr.zig").LineupManager;
|
||||||
const SceneManager = @import("../manager/scene_mgr.zig").SceneManager;
|
const SceneManager = @import("../manager/scene_mgr.zig").SceneManager;
|
||||||
|
const CacheScene = @import("../manager/scene_mgr.zig");
|
||||||
|
|
||||||
const ArrayList = std.ArrayList;
|
const ArrayList = std.ArrayList;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
@ -38,7 +39,8 @@ pub fn onSceneEntityMove(session: *Session, packet: *const Packet, allocator: Al
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn onEnterScene(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
pub fn onEnterScene(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
const entrance_config = try Config.loadMapEntranceConfig(allocator, "resources/MapEntrance.json");
|
const entrance_config = &CacheScene.global_game_config_cache.map_entrance_config;
|
||||||
|
|
||||||
const req = try packet.getProto(protocol.EnterSceneCsReq, allocator);
|
const req = try packet.getProto(protocol.EnterSceneCsReq, allocator);
|
||||||
var lineup_mgr = LineupManager.init(allocator);
|
var lineup_mgr = LineupManager.init(allocator);
|
||||||
const lineup = try lineup_mgr.createLineup();
|
const lineup = try lineup_mgr.createLineup();
|
||||||
|
@ -71,7 +73,7 @@ pub fn onEnterScene(session: *Session, packet: *const Packet, allocator: Allocat
|
||||||
|
|
||||||
pub fn onGetSceneMapInfo(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
pub fn onGetSceneMapInfo(session: *Session, packet: *const Packet, allocator: Allocator) !void {
|
||||||
const req = try packet.getProto(protocol.GetSceneMapInfoCsReq, allocator);
|
const req = try packet.getProto(protocol.GetSceneMapInfoCsReq, allocator);
|
||||||
const res_config = try Res_config.anchorLoader(allocator, "resources/res.json");
|
const cached_res_config = &CacheScene.global_game_config_cache.res_config;
|
||||||
const ranges = [_][2]usize{
|
const ranges = [_][2]usize{
|
||||||
.{ 0, 101 },
|
.{ 0, 101 },
|
||||||
.{ 10000, 10051 },
|
.{ 10000, 10051 },
|
||||||
|
@ -94,7 +96,7 @@ pub fn onGetSceneMapInfo(session: *Session, packet: *const Packet, allocator: Al
|
||||||
map_info.entry_id = @intCast(floor_id);
|
map_info.entry_id = @intCast(floor_id);
|
||||||
map_info.floor_id = @intCast(floor_id);
|
map_info.floor_id = @intCast(floor_id);
|
||||||
map_info.cur_map_entry_id = @intCast(floor_id);
|
map_info.cur_map_entry_id = @intCast(floor_id);
|
||||||
for (res_config.scene_config.items) |sceneConf| {
|
for (cached_res_config.scene_config.items) |sceneConf| {
|
||||||
if (sceneConf.planeID != floor_id / 1000) continue;
|
if (sceneConf.planeID != floor_id / 1000) continue;
|
||||||
try map_info.unlock_teleport_list.ensureUnusedCapacity(sceneConf.teleports.items.len);
|
try map_info.unlock_teleport_list.ensureUnusedCapacity(sceneConf.teleports.items.len);
|
||||||
try map_info.maze_prop_list.ensureUnusedCapacity(sceneConf.props.items.len);
|
try map_info.maze_prop_list.ensureUnusedCapacity(sceneConf.props.items.len);
|
||||||
|
@ -125,7 +127,7 @@ pub fn onGetSceneMapInfo(session: *Session, packet: *const Packet, allocator: Al
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try rsp.scene_map_info.append(map_info);
|
try rsp.scene_map_info.append(map_info);
|
||||||
try session.send(CmdID.CmdGetSceneMapInfoScRsp, rsp);
|
try session.send(protocol.CmdID.CmdGetSceneMapInfoScRsp, rsp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn onGetUnlockTeleport(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
pub fn onGetUnlockTeleport(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
|
@ -150,8 +152,7 @@ pub fn onEnterSection(session: *Session, packet: *const Packet, allocator: Alloc
|
||||||
pub fn onGetEnteredScene(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
pub fn onGetEnteredScene(session: *Session, _: *const Packet, allocator: Allocator) !void {
|
||||||
var rsp = protocol.GetEnteredSceneScRsp.init(allocator);
|
var rsp = protocol.GetEnteredSceneScRsp.init(allocator);
|
||||||
var noti = protocol.EnteredSceneChangeScNotify.init(allocator);
|
var noti = protocol.EnteredSceneChangeScNotify.init(allocator);
|
||||||
|
const entrance_config = &CacheScene.global_game_config_cache.map_entrance_config;
|
||||||
const entrance_config = try Config.loadMapEntranceConfig(allocator, "resources/MapEntrance.json");
|
|
||||||
for (entrance_config.map_entrance_config.items) |entrance| {
|
for (entrance_config.map_entrance_config.items) |entrance| {
|
||||||
try rsp.entered_scene_info_list.append(protocol.EnteredSceneInfo{
|
try rsp.entered_scene_info_list.append(protocol.EnteredSceneInfo{
|
||||||
.floor_id = entrance.floor_id,
|
.floor_id = entrance.floor_id,
|
||||||
|
|
16
hotfix.json
16
hotfix.json
|
@ -6,5 +6,21 @@
|
||||||
"ifix_version": "0",
|
"ifix_version": "0",
|
||||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10872485_88661d25b99c_8af3232d484baf",
|
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10872485_88661d25b99c_8af3232d484baf",
|
||||||
"lua_version": ""
|
"lua_version": ""
|
||||||
|
},
|
||||||
|
"CNBETAWin3.4.52": {
|
||||||
|
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_10958287_efcd7e2ecb36_41640921a4a7ad",
|
||||||
|
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_10958287_6c138ff4c1b7_38b93698a31d0a",
|
||||||
|
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10894308_758b6d45fde6_ac9089c9b1e0a0",
|
||||||
|
"ifix_version": "0",
|
||||||
|
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_10958408_e8e889b9db41_0897ed66246acc",
|
||||||
|
"lua_version": ""
|
||||||
|
},
|
||||||
|
"CNBETAWin3.4.53": {
|
||||||
|
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_11042804_ec670e5793f5_b9cbdedb0f66c3",
|
||||||
|
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_11042804_e1fd49abc9a3_c60b351cb94b11",
|
||||||
|
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_10894308_758b6d45fde6_ac9089c9b1e0a0",
|
||||||
|
"ifix_version": "0",
|
||||||
|
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_11043241_a4527ee0b70c_d6ce1726d05175",
|
||||||
|
"lua_version": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
773
resources/BuffInfoConfig.json
Normal file
773
resources/BuffInfoConfig.json
Normal file
|
@ -0,0 +1,773 @@
|
||||||
|
{
|
||||||
|
"text_map_config": [
|
||||||
|
{
|
||||||
|
"GroupID": 3001,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111008,
|
||||||
|
"name": "Increases all allies' Break Effect by 50%. After dealing Super Break DMG to enemies, increases SPD by an amount equal to 40% of current SPD, lasting until the next action."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111010,
|
||||||
|
"name": "Increases DMG dealt by all allies' Ultimates by 50%. After defeating an enemy target, the attacker's action is Advanced Forward by 25%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111011,
|
||||||
|
"name": "Before enemy units with Steadfast Safeguard are Weakness Broken, tally the amount of DoT they receive. When a unit is Weakness Broken, deal to this unit Additional DMG equal to 170% of the tallied DoT."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111008,
|
||||||
|
"name": "Increases all allies' Break Effect by 50%. After dealing Super Break DMG to enemies, increases SPD by an amount equal to 40% of current SPD, lasting until the next action."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111009,
|
||||||
|
"name": "Increases DMG dealt by all allies' Basic ATK and Skills by 50%. After defeating an enemy target, the attacker's action is Advanced Forward by 25%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111012,
|
||||||
|
"name": "Before enemy units with Steadfast Safeguard are Weakness Broken, tally the amount of Follow-up ATK DMG they receive. When the unit is Weakness Broken, deal to this unit Additional DMG equal to 200% of the tallied DMG."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3002,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111015,
|
||||||
|
"name": "Follow-up ATKs/Counters will also reduce the Toughness of targets that do not have the corresponding Weakness, with the effect being equivalent to 50%/100% of the original Toughness Reduction from abilities."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111013,
|
||||||
|
"name": "The DMG dealt by all allies' Basic ATK increases by 25%, with an additional increase of 25% against targets that are Weakness Broken."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111014,
|
||||||
|
"name": "When an ally breaks an enemy target's Weakness, Advances Action Forward by 25%."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111015,
|
||||||
|
"name": "Follow-up ATKs/Counters will also reduce the Toughness of targets that do not have the corresponding Weakness, with the effect being equivalent to 50%/100% of the original Toughness Reduction from abilities."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111016,
|
||||||
|
"name": "Increases Break DMG taken by all enemies by 20%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111017,
|
||||||
|
"name": "Increases CRIT DMG dealt by all allies' Skill by 40%, with an additional increase of 40% against targets who are Weakness Broken."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3003,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111021,
|
||||||
|
"name": "Follow-up ATK DMG dealt by characters ignores 15% of the enemy target's All-Type RES and can also reduce the Toughness of targets that do not have the corresponding Weakness, with the effect being equivalent to 50% of the original Toughness Reduction from the ability."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111022,
|
||||||
|
"name": "Character's Ice DMG dealt increases by 55%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111023,
|
||||||
|
"name": "When an ally breaks an enemy target's Weakness, Advance their Action Forward by 15%. This effect can trigger a max of #2[i] time(s) per turn."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111018,
|
||||||
|
"name": "When characters attack Weakness Broken enemies, all DoTs currently applied to the enemies will immediately deal DMG equal to 80% of the original DMG. This effect can trigger a max of #2[i] time(s) on each character in a single turn."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111019,
|
||||||
|
"name": "After defeating enemy targets, increase Break DMG taken by all enemies by 5% to a max of #2[i] stack(s). This effect will also take effect on new enemies entering the battlefield."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111020,
|
||||||
|
"name": "Increases the CRIT DMG of Ultimate DMG dealt by 30%, with an additional increase of 30% against enemy targets that are Weakness Broken."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3004,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111028,
|
||||||
|
"name": "Increases the DMG dealt by all allies' Basic ATKs by 50%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111027,
|
||||||
|
"name": "After defeating any enemy target, advances all ally targets' actions by 10%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111021,
|
||||||
|
"name": "Follow-up ATK DMG dealt by characters ignores 15% of the enemy target's All-Type RES and can also reduce the Toughness of targets that do not have the corresponding Weakness, with the effect being equivalent to 50% of the original Toughness Reduction from the ability."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111024,
|
||||||
|
"name": "After a character uses Follow-up ATK, advances their action by 15%. This effect can be triggered a max of #2[i] time(s) per character before their next action."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111025,
|
||||||
|
"name": "The Weakness Break Efficiency increases by 25% when characters deal Ultimate DMG."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111026,
|
||||||
|
"name": "Increases all ally characters' Break Effect by 50%. When a character Breaks an enemy target's Weakness, regenerates #2[i] Energy to the character."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3005,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111030,
|
||||||
|
"name": "Increases all enemies' Break DMG taken by 10%. After Breaking an enemy's Weakness, increases all allies' SPD by 20% for #3[i] turn(s)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111033,
|
||||||
|
"name": "Before enemy units with \"Steadfast Safeguard\" are Weakness Broken, tally the amount of DoT they receive. When a unit is Weakness Broken, deals Additional DMG to this unit equal to 200% of the tallied DoT."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111034,
|
||||||
|
"name": "Increases Quantum DMG dealt by all allies' by 60%. After defeating an enemy target, advances the attacker's action by 15%."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111029,
|
||||||
|
"name": "When using Skill or Ultimate on one ally target, increases the ability target's ATK by 25%, lasting for #2[i] turn(s). This effect can stack up to #3[i] time(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111032,
|
||||||
|
"name": "Increases Ultimate DMG dealt by all allies to Weakness Broken enemies by 70%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111031,
|
||||||
|
"name": "Increases Basic ATK and Skill DMG dealt by all allies by 55%."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3006,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111035,
|
||||||
|
"name": "When 2 or more characters following the Path of Erudition are in the team, increases all allies' ATK by 60%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111036,
|
||||||
|
"name": "Increases Physical DMG dealt by all allies by 50% and Effect RES by 50%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111037,
|
||||||
|
"name": "When an ally unit Breaks an Enemy target's Weakness, advances the ally unit's action by 20%."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111038,
|
||||||
|
"name": "When characters attack Weakness Broken enemies, all DoTs currently applied to the enemies will immediately deal DMG equal to 60% of the original DMG. This effect can trigger a max of #2[i] time(s) on each character in a single turn."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111039,
|
||||||
|
"name": "After defeating enemy targets, increase Break DMG taken by all enemies by 5% to a max of #2[i] stack(s). This effect will also take effect on new enemies entering the battlefield."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111040,
|
||||||
|
"name": "Increases CRIT DMG of all allies' Basic ATK and Ultimate DMG dealt by 30%, with an additional increase of 30% against enemy targets that are Weakness Broken."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3007,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111041,
|
||||||
|
"name": "Increases DMG dealt by all allies' Basic ATK by 50%. After defeating an enemy target, advances the attacker's action by 10%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111042,
|
||||||
|
"name": "Increases the SPD of all allies by 15% and reduces their DMG taken by 15%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111043,
|
||||||
|
"name": "Follow-up ATK DMG dealt by characters ignores 15% of the enemy target's All-Type RES and can also reduce the Toughness of targets that do not have the corresponding Weakness, with the effect being equivalent to 50% of the original Toughness Reduction from the ability."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111044,
|
||||||
|
"name": "When using Skill or Ultimate on one ally target, increases the ability target's CRIT DMG by 35%, lasting for #2[i] turn(s). This effect can stack up to #3[i] time(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111045,
|
||||||
|
"name": "Increases all enemies' Break DMG taken by 10%. After Breaking an enemy's Weakness, increases all allies' SPD by 20% for #3[i] turn(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111046,
|
||||||
|
"name": "Increases all allies' Skill DMG and Ultimate DMG dealt by 50%."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3008,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111047,
|
||||||
|
"name": "While any character following the Path of Erudition is in the team, increases all allies' All-Type RES PEN by 15%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111048,
|
||||||
|
"name": "Increases Ultimate DMG dealt by all allies by 50% and Effect RES by 50%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111049,
|
||||||
|
"name": "After any ally target consumes their own HP, increases their DMG dealt by 55% for #2[i] turn(s)."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111050,
|
||||||
|
"name": "When any ally target Breaks an enemy target's Weakness, increases all ally targets' SPD by 20% for #2[i] turn(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111051,
|
||||||
|
"name": "After any ally target's HP was consumed, they restore HP equal to 60% of the HP consumed. This can restore a maximum of 50% HP per turn."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111052,
|
||||||
|
"name": "Increases ally targets' Follow-up ATK DMG dealt by 30%, with an additional increase of 30% against enemy targets that are Weakness Broken."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3009,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111053,
|
||||||
|
"name": "Increases memosprite's Weakness Break Efficiency by 50%. Ignores 20% of the target's DEF when memosprite deals DMG."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111018,
|
||||||
|
"name": "When characters attack Weakness Broken enemies, all DoTs currently applied to the enemies will immediately deal DMG equal to 80% of the original DMG. This effect can trigger a max of #2[i] time(s) on each character in a single turn."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111020,
|
||||||
|
"name": "Increases the CRIT DMG of Ultimate DMG dealt by 30%, with an additional increase of 30% against enemy targets that are Weakness Broken."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111055,
|
||||||
|
"name": "Increases Physical DMG dealt by all allies by 50%. When a unit uses a Skill, restores 20% of that unit's HP."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111021,
|
||||||
|
"name": "Follow-up ATK DMG dealt by characters ignores 15% of the enemy target's All-Type RES and can also reduce the Toughness of targets that do not have the corresponding Weakness, with the effect being equivalent to 50% of the original Toughness Reduction from the ability."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111030,
|
||||||
|
"name": "Increases all enemies' Break DMG taken by 10%. After Breaking an enemy's Weakness, increases all allies' SPD by 20% for #3[i] turn(s)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3010,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111056,
|
||||||
|
"name": "The DMG dealt by the character in position 1 in the lineup increases by 60%. For every #2[i] Skill Points consumed, 1 Skill Point is recovered."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111057,
|
||||||
|
"name": "After any ally target consumes their own HP, increases their DMG dealt by 40% for #2[i] turn(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111058,
|
||||||
|
"name": "After an ally target deals DoT to an enemy, #1[i] Energy is regenerated. DoT of all allies have 30% All-Type RES against enemy targets."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111046,
|
||||||
|
"name": "Increases all allies' Skill DMG and Ultimate DMG dealt by 50%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111014,
|
||||||
|
"name": "When an ally breaks an enemy target's Weakness, Advances Action Forward by 25%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111021,
|
||||||
|
"name": "Follow-up ATK DMG dealt by characters ignores 15% of the enemy target's All-Type RES and can also reduce the Toughness of targets that do not have the corresponding Weakness, with the effect being equivalent to 50% of the original Toughness Reduction from the ability."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3011,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111062,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111063,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111061,
|
||||||
|
"name": "Unknown"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111059,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111060,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111064,
|
||||||
|
"name": "Unknown"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 3012,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3111062,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111063,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111061,
|
||||||
|
"name": "Unknown"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3111059,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111060,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3111064,
|
||||||
|
"name": "Unknown"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2001,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031301,
|
||||||
|
"name": "When allies use an Ultimate to attack targets, inflicts 2 stacks of Shatter to the target."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031302,
|
||||||
|
"name": "When enemies enter battle, there is a 80% chance to instantly receive 1 stack of Shatter."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031303,
|
||||||
|
"name": "When enemy targets with Shatter are defeated, increases DMG dealt by Ultimate for all allies in this battle by 15%, stacking up to #2[i] times."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2002,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031304,
|
||||||
|
"name": "When an enemy takes DoT, adjacent targets also take DoT of the same type by an amount equal to 60% of the original DMG dealt."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031305,
|
||||||
|
"name": "After allies attack enemies, inflicts Wind Shear on the target, causing the target to take a set amount of Wind DoT at the start of every turn."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031306,
|
||||||
|
"name": "When an enemy takes DoT, the ally with the lowest HP percentage will heal for 10% of their Max HP, and their action will be Advanced Forward by 8%."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2003,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031307,
|
||||||
|
"name": "Characters' Follow-up ATKs deal 50% increased DMG, and this effect will be triggered 1 additional time after triggering Whimsicality's effect."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031308,
|
||||||
|
"name": "When Ultimate deals DMG to the enemy target, it will be considered as a Follow-up ATK."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031309,
|
||||||
|
"name": "Whenever a character hits an enemy with a Follow-up ATK, all allies' actions are Advanced Forward by 14%. This effect can only be triggered once for each attack."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2004,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031310,
|
||||||
|
"name": "When allies use a Skill to attack enemy targets, there is a 80% fixed chance to inflict Shatter to the target."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031311,
|
||||||
|
"name": "When enemy targets use their abilities, inflicts 1 stack of Scalded on them and adjacent units. While in the Scalded state, targets receive 10% more DMG, stacking up to #2[i] stack(s). This effect is effective on the set amount of DMG from the Whimsicality effect."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031312,
|
||||||
|
"name": "After characters use an Ultimate to attack, their action is Advanced Forward by 25% and deals 100% more DMG, lasting for #1[i] turn(s)."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2005,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031313,
|
||||||
|
"name": "Whimsicality no longer requires accumulated DMG dealt by Follow-up ATK. Instead, it triggers after allies use #1[i] instances of Follow-up ATK and the DMG dealt by this effect increases by 60%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031314,
|
||||||
|
"name": "When the Whimsicality effect is triggered, regenerates Energy for all allies equal to 35% of their respective Max Energy. Energy recovered via this effect can exceed the ally's Max Energy."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031315,
|
||||||
|
"name": "Apart from Follow-up ATKs, all other kinds of DMG dealt by allies can also accumulate progress equal to 18% of the original DMG amount."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2006,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031316,
|
||||||
|
"name": "When enemy targets are defeated, the DoTs on them have a 100% base chance to be transferred to all enemies."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031317,
|
||||||
|
"name": "After enemy targets enter battle, they become afflicted by Wind Shear. At the start of every turn, they receive a set amount of Wind DoT and inflict Wind Shear on themselves and adjacent enemy targets."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031318,
|
||||||
|
"name": "When 3 or more characters following the Path of Nihility are in the team, increases all allies' DMG dealt by 60% and SPD by 30%."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2007,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031319,
|
||||||
|
"name": "Reduces the Charge points required to trigger Whimsicality by #1[i] and deals DMG #2[i] more time(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031320,
|
||||||
|
"name": "Increases DMG dealt from allies by Ultimate by 30%. After the Ultimate is used, Whimsicality additionally gains #2[i] point(s) of Charge."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031321,
|
||||||
|
"name": "After allies attack enemy targets, Whimsicality additionally gains #1[i] point(s) of Charge."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2008,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031303,
|
||||||
|
"name": "When enemy targets with Shatter are defeated, increases DMG dealt by Ultimate for all allies in this battle by 15%, stacking up to #2[i] times."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031322,
|
||||||
|
"name": "Increases DMG dealt with Shatter by 20%. When triggered, applies 1 stack of Shatter to adjacent targets."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031323,
|
||||||
|
"name": "When allies' Follow-up ATKs hit enemy targets, there is a 60% fixed chance to apply 1 stack of Shatter to the target. This effect can only be triggered 1 time per attack per enemy target."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2009,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031324,
|
||||||
|
"name": "Increases the Break DMG taken by all enemies by 10%. If allies dealt Super Break DMG to enemies after using an attack, additionally Charges Whimsicality by #2[i] points."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031325,
|
||||||
|
"name": "Follow-up ATK DMG dealt increases by 50%. Follow-up ATKs' Weakness Break Efficiency increases by 50%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031326,
|
||||||
|
"name": "When characters launch attacks by using their Ultimate, they will ignore the enemy targets' Weakness to cause Toughness Reduction."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2010,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031330,
|
||||||
|
"name": "Increases all allies' Break Effect by 30%. When Breaking enemy targets, all enemies become afflicted with Wind Shear, taking a set amount of Wind DoT at the start of every turn.\\nAll enemies become immune to Crowd Control debuffs."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031331,
|
||||||
|
"name": "After any ally target uses Ultimate to attack any enemy target and if the target currently has 1/2/3 types of DoTs, each currently active DoT immediately deals DMG equal to 60% / 100% / 150% of the original DMG."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031305,
|
||||||
|
"name": "After allies attack enemies, inflicts Wind Shear on the target, causing the target to take a set amount of Wind DoT at the start of every turn."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2011,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031327,
|
||||||
|
"name": "During Surging Grit, all ally Skills do not consume Skill Points. After characters use Skills or Ultimate, they gain 1 stack of \"Feverish Surge\" for every enemy target hit. These stacks will increase said characters' Skill and Ultimate DMG by 4% and SPD by 4% to a max of #3[i] stack(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031328,
|
||||||
|
"name": "During Surging Grit, increases all allies' Follow-up ATK DMG by 40%. After triggering a Follow-up ATK, deals a set amount of DMG to every attacked enemy target and adjacent target."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031329,
|
||||||
|
"name": "During Surging Grit, increases Break DMG received by enemy targets by 30%. When characters Break enemy targets' Weaknesses, advances the character's action by 12%."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3031202,
|
||||||
|
"name": "After allies use Skill to attack enemy targets, every enemy target hit additionally accumulates #1[i] Grit Value for allies."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031203,
|
||||||
|
"name": "At the start of battle, after an ally uses their Skill to attack an enemy target, deals a set amount of DMG to all attacked enemy targets."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031204,
|
||||||
|
"name": "Upon entering Surging Grit, advances all allies actions by #1[i]% and increases DMG received by enemy targets during this period by #2[i]%. Additionally, after an ally uses their Skill to attack an enemy target, deals an additional set amount of DMG to all attacked enemy targets and adjacent targets."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2012,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031333,
|
||||||
|
"name": "During Surging Grit, all allies' SPD increases by 40%. When all \"Resound\" is consumed in an attack, recover 1 Skill Point."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031332,
|
||||||
|
"name": "During Surging Grit, after an enemy target is defeated by any unit, there is a 50% chance for all allies to gain 1 stack of \"Resound.\""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031334,
|
||||||
|
"name": "When entering Surging Grit, activates the Ultimate of the first character on the team lineup and increases their DMG dealt by Ultimate during Surging Grit by 50%."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3031207,
|
||||||
|
"name": "After allies use Ultimate to attack enemy targets, every enemy target hit additionally accumulates #1[i] Grit Value for allies."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031208,
|
||||||
|
"name": "After an enemy target is defeated by any unit, all allies gain 1 stack of \"Resound.\""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031209,
|
||||||
|
"name": "When entering Surging Grit, consume all \"Resound.\" Each stack of \"Resound\" deals 1 instance of a set amount of DMG to a random enemy target. During Surging Grit, increases enemy targets' DMG taken by #3[i]%, and after they are defeated by any unit, all allies additionally gain 1 stack of \"Resound.\" When \"Resound\" stacks up to #1[i] or higher, consumes all \"Resound\" stacks and deals 1 instance of a set amount of DMG to a random enemy target per stack."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2013,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031335,
|
||||||
|
"name": "During Surging Grit, after allies use their Skill or launch a Follow-up ATK against enemy targets with \"Shatter,\" \"Shatter\" can be immediately triggered."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031336,
|
||||||
|
"name": "During Surging Grit, increases Break DMG taken by enemy targets by 30%. When targets in the \"Shatter\" state are defeated by any unit, deals #1[i] Toughness Reduction regardless of Weakness Type to adjacent enemy targets."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031337,
|
||||||
|
"name": "During Surging Grit, when any character uses their Skill, consumes HP equal to 20% of their current HP. Additionally, after using Skill, restores HP equal to 20% of their Max HP."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3031212,
|
||||||
|
"name": "After an ally uses Basic ATK or Ultimate to attack enemy targets, every enemy target hit additionally accumulates #1[i] Grit Value for allies."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031213,
|
||||||
|
"name": "When allies use their Basic ATK or Ultimate to attack any enemy targets, inflicts the target with \"Shatter,\" stacking up to #1[i] time(s). At the start of the target's turn or when the target is defeated by any unit, deals True DMG to the target based on the number of \"Shatter\" stacks."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031214,
|
||||||
|
"name": "Upon entering Surging Grit, immediately regenerates #1[i]% Energy for all allies. The Energy regenerated by this effect can exceed the target's Max Energy. Increases DMG taken by enemy targets during this period by #2[i]%. Additionally, triggering \"Shatter\" will deal True DMG to all attacked enemy targets and adjacent targets."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2014,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031327,
|
||||||
|
"name": "During Surging Grit, all ally Skills do not consume Skill Points. After characters use Skills or Ultimate, they gain 1 stack of \"Feverish Surge\" for every enemy target hit. These stacks will increase said characters' Skill and Ultimate DMG by 4% and SPD by 4% to a max of #3[i] stack(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031338,
|
||||||
|
"name": "During Surging Grit, when any ally uses their Skill, consumes HP equal to 20% of their Max HP. Then, after using Skill, restores HP equal to 20% of their Max HP."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031329,
|
||||||
|
"name": "During Surging Grit, increases Break DMG received by enemy targets by 30%. When characters Break enemy targets' Weaknesses, advances the character's action by 12%."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3031202,
|
||||||
|
"name": "After allies use Skill to attack enemy targets, every enemy target hit additionally accumulates #1[i] Grit Value for allies."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031203,
|
||||||
|
"name": "At the start of battle, after an ally uses their Skill to attack an enemy target, deals a set amount of DMG to all attacked enemy targets."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031204,
|
||||||
|
"name": "Upon entering Surging Grit, advances all allies actions by #1[i]% and increases DMG received by enemy targets during this period by #2[i]%. Additionally, after an ally uses their Skill to attack an enemy target, deals an additional set amount of DMG to all attacked enemy targets and adjacent targets."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2015,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031333,
|
||||||
|
"name": "During Surging Grit, all allies' SPD increases by 40%. When all \"Resound\" is consumed in an attack, recover 1 Skill Point."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031343,
|
||||||
|
"name": "All allies can also reduce Toughness when attacking enemies that don't have the corresponding Weakness Type, with the effect equivalent to 50% of the original Toughness Reduction value. This cannot stack with other Toughness Reduction effects that also ignore Weakness Type. During Surging Grit, the Break DMG received by enemy targets increases by 30%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031339,
|
||||||
|
"name": "After consuming \"Resound\" to deal DMG, increases all allies' DMG dealt by 60% for #2[i] turn(s)."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3031207,
|
||||||
|
"name": "After allies use Ultimate to attack enemy targets, every enemy target hit additionally accumulates #1[i] Grit Value for allies."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031208,
|
||||||
|
"name": "After an enemy target is defeated by any unit, all allies gain 1 stack of \"Resound.\""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031209,
|
||||||
|
"name": "When entering Surging Grit, consume all \"Resound.\" Each stack of \"Resound\" deals 1 instance of a set amount of DMG to a random enemy target. During Surging Grit, increases enemy targets' DMG taken by #3[i]%, and after they are defeated by any unit, all allies additionally gain 1 stack of \"Resound.\" When \"Resound\" stacks up to #1[i] or higher, consumes all \"Resound\" stacks and deals 1 instance of a set amount of DMG to a random enemy target per stack."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2016,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031340,
|
||||||
|
"name": "During the \"Surging Grit\" period, the ally targets' DoT dealt ignores 20% of the target's All-Type RES. When the enemy target receives DoT, increases all allies DoT dealt by 1%. Up to a max of #3[i] stack(s). Lasts until \"Surging Grit\" ends."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031341,
|
||||||
|
"name": "During Surging Grit, Skill DMG dealt by ally targets increases by 50%. When attacking targets with special Wind Shear \"Echo Enigma,\" each stack of \"Echo Enigma\" additionally increases Skill DMG by 10%."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031342,
|
||||||
|
"name": "Every stack of \"Echo Enigma\" increases enemy target's DMG received by 3%. During the Surging Grit period, when enemy targets enter battle, and after they take action, additionally adds #2[i] stack(s) of special Wind Shear \"Echo Enigma.\""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3031217,
|
||||||
|
"name": "Every time a DoT is received by an enemy target, additionally accumulates #1[i] Grit Value for allies."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031218,
|
||||||
|
"name": "When an enemy target enters battle, they are inflicted with #1[i] stack(s) of \"Echo Enigma,\" which deals a special kind of Wind Shear, and they suffer #2[i] stack(s) of \"Echo Enigma\" after acting, receiving Wind DoT at the start of each turn. Goes up to #3[i] stack(s) at max for #4[i] turn(s)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031219,
|
||||||
|
"name": "Entering the \"Surging Grit\" state causes all enemies to suffer #5[i] stack(s) of special Wind Shear \"Echo Enigma,\" and causes all currently suffered DoT to immediately produce DMG equal to #1[i]% of the original DMG. During the \"Surging Grit\" period, increases enemy targets' DMG received by #2[i]%, reduces DMG dealt by #3[i]%, and increases SPD by #4[i]%."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GroupID": 2017,
|
||||||
|
"BuffList1": [
|
||||||
|
{
|
||||||
|
"id": 3031344,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031345,
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031346,
|
||||||
|
"name": "Unknown"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"BuffList2": [
|
||||||
|
{
|
||||||
|
"id": 3031202,
|
||||||
|
"name": "After allies use Skill to attack enemy targets, every enemy target hit additionally accumulates #1[i] Grit Value for allies."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031203,
|
||||||
|
"name": "At the start of battle, after an ally uses their Skill to attack an enemy target, deals a set amount of DMG to all attacked enemy targets."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3031204,
|
||||||
|
"name": "Upon entering Surging Grit, advances all allies actions by #1[i]% and increases DMG received by enemy targets during this period by #2[i]%. Additionally, after an ally uses their Skill to attack an enemy target, deals an additional set amount of DMG to all attacked enemy targets and adjacent targets."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -6076,6 +6076,34 @@
|
||||||
},
|
},
|
||||||
"RewardID": 201
|
"RewardID": 201
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"CanReview": true,
|
||||||
|
"FinishTriggerParams": [
|
||||||
|
{
|
||||||
|
"TriggerParam": "104061103",
|
||||||
|
"TriggerType": "FinishSubMission"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"GroupID": 2245,
|
||||||
|
"MessageText": {
|
||||||
|
"Hash": 530566966,
|
||||||
|
"hash64": 6926758272334550000
|
||||||
|
},
|
||||||
|
"Order": 433,
|
||||||
|
"RewardID": 201,
|
||||||
|
"TriggerParams": [
|
||||||
|
{
|
||||||
|
"TriggerParam": "20451060",
|
||||||
|
"TriggerType": "EnterBattle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TutorialGuideIDList": [
|
||||||
|
224501,
|
||||||
|
224502,
|
||||||
|
224503
|
||||||
|
],
|
||||||
|
"TutorialType": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"TutorialGuideIDList": [
|
"TutorialGuideIDList": [
|
||||||
932301,
|
932301,
|
||||||
|
|
Loading…
Reference in a new issue