fix(scene): unlock all teleports across maps, merge resource configs, fix scenary not load error, and add quick farm battle handlers
This commit is contained in:
1 parent
9aab62414f
commit
163d4433e6
47 files changed
+2024977
-5992
No files matched your search
+160
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"avatar_config": [
|
||||
{
|
||||
"name": "1503",
|
||||
"id": 1503,
|
||||
"hp": 100,
|
||||
"sp": 50,
|
||||
"level": 80,
|
||||
"promotion": 6,
|
||||
"rank": 0,
|
||||
"lightcone": {
|
||||
"id": 23055,
|
||||
"rank": 1,
|
||||
"level": 80,
|
||||
"promotion": 6
|
||||
},
|
||||
"relics": [
|
||||
"61331,15,1,4,7:3:2,3:1:2,6:3:3,4:2:0",
|
||||
"61332,15,1,4,6:1:1,8:3:3,5:2:1,7:3:5",
|
||||
"61333,15,6,4,7:3:3,6:1:2,1:3:4,2:2:2",
|
||||
"61334,15,4,4,6:1:1,1:2:2,11:4:5,8:2:1",
|
||||
"63085,15,3,4,7:3:3,11:3:3,10:2:2,12:1:2",
|
||||
"63086,15,2,4,2:1:1,7:3:5,6:3:4,8:2:2"
|
||||
],
|
||||
"use_technique": true
|
||||
},
|
||||
{
|
||||
"name": "1511",
|
||||
"id": 1511,
|
||||
"hp": 100,
|
||||
"sp": 50,
|
||||
"level": 80,
|
||||
"promotion": 6,
|
||||
"rank": 6,
|
||||
"lightcone": {
|
||||
"id": 23065,
|
||||
"rank": 5,
|
||||
"level": 80,
|
||||
"promotion": 6
|
||||
},
|
||||
"relics": [
|
||||
"61331,15,1,4,9:2:3,7:2:1,10:2:1,8:3:1",
|
||||
"61332,15,1,4,9:1:2,6:4:1,8:2:3,7:2:2",
|
||||
"61333,15,5,4,1:2:4,7:2:2,8:2:4,4:3:4",
|
||||
"61334,15,4,4,9:2:2,1:2:1,11:4:4,8:1:2",
|
||||
"63295,15,1,4,8:3:3,3:2:4,9:2:2,7:2:2",
|
||||
"63296,15,3,4,7:1:1,1:4:2,11:1:2,8:3:4"
|
||||
],
|
||||
"use_technique": true
|
||||
},
|
||||
{
|
||||
"name": "Trailblazer(Harmony)",
|
||||
"id": 8006,
|
||||
"hp": 100,
|
||||
"sp": 50,
|
||||
"level": 80,
|
||||
"promotion": 6,
|
||||
"rank": 6,
|
||||
"lightcone": {
|
||||
"id": 23019,
|
||||
"rank": 5,
|
||||
"level": 80,
|
||||
"promotion": 6
|
||||
},
|
||||
"relics": [
|
||||
"61181,15,1,4,12:6:12,7:1:2,4:1:2,6:1:2",
|
||||
"61182,15,1,4,7:1:2,12:6:12,6:1:2,4:1:2",
|
||||
"61183,15,3,4,7:1:2,12:6:12,1:1:2,4:1:2",
|
||||
"61184,15,4,4,12:6:12,6:1:2,4:1:2,1:1:2",
|
||||
"63075,15,1,4,12:4:8,7:3:6,6:1:2,1:1:2",
|
||||
"63076,15,1,4,7:6:12,1:1:2,4:1:2,6:1:2"
|
||||
],
|
||||
"use_technique": true
|
||||
},
|
||||
{
|
||||
"name": "Trailblazer(Remembrance)",
|
||||
"id": 8008,
|
||||
"hp": 100,
|
||||
"sp": 50,
|
||||
"level": 80,
|
||||
"promotion": 6,
|
||||
"rank": 6,
|
||||
"lightcone": {
|
||||
"id": 24005,
|
||||
"rank": 5,
|
||||
"level": 80,
|
||||
"promotion": 6
|
||||
},
|
||||
"relics": [
|
||||
"61231,15,1,4,9:2:4,7:1:2,5:5:10,2:1:2",
|
||||
"61232,15,1,4,7:1:2,9:6:12,5:1:2,4:1:2",
|
||||
"61233,15,5,4,5:6:12,2:1:2,7:1:2,4:1:2",
|
||||
"61234,15,4,4,9:6:12,5:1:2,2:1:2,4:1:2",
|
||||
"63175,15,6,4,9:3:6,5:1:2,2:1:2,7:4:8",
|
||||
"63176,15,2,4,5:1:2,7:6:12,9:1:2,2:1:2"
|
||||
],
|
||||
"use_technique": true
|
||||
},
|
||||
{
|
||||
"name": "Trailblazer(Elation)",
|
||||
"id": 8010,
|
||||
"hp": 100,
|
||||
"sp": 50,
|
||||
"level": 80,
|
||||
"promotion": 6,
|
||||
"rank": 6,
|
||||
"lightcone": {
|
||||
"id": 24006,
|
||||
"rank": 5,
|
||||
"level": 80,
|
||||
"promotion": 6
|
||||
},
|
||||
"relics": [
|
||||
"61301,15,1,4,5:1:2,7:6:12,8:1:2,9:1:2",
|
||||
"61302,15,1,4,5:1:2,7:2:4,8:5:10,9:1:2",
|
||||
"61303,15,4,4,5:1:2,2:1:2,7:1:2,9:6:12",
|
||||
"61304,15,4,4,2:1:2,5:1:2,8:2:4,9:5:10",
|
||||
"63255,15,2,4,2:1:2,7:1:2,8:1:2,9:6:12",
|
||||
"63256,15,2,4,5:1:2,7:1:2,8:1:2,9:6:12"
|
||||
],
|
||||
"use_technique": true
|
||||
},
|
||||
{
|
||||
"name": "Acheron",
|
||||
"id": 1308,
|
||||
"hp": 100,
|
||||
"sp": 50,
|
||||
"level": 80,
|
||||
"promotion": 6,
|
||||
"rank": 0,
|
||||
"lightcone": {
|
||||
"id": 23024,
|
||||
"rank": 1,
|
||||
"level": 80,
|
||||
"promotion": 6
|
||||
},
|
||||
"relics": [
|
||||
"61101,15,1,4,8:3:3,7:2:3,9:2:1,5:2:1",
|
||||
"61102,15,1,4,7:2:1,8:2:4,9:3:2,1:2:4",
|
||||
"61103,15,5,4,8:2:1,3:3:3,10:2:3,7:2:3",
|
||||
"61104,15,4,4,3:2:0,8:3:3,4:2:1,9:2:3",
|
||||
"63145,15,7,4,7:1:0,8:3:2,5:2:4,9:3:3",
|
||||
"63146,15,4,4,4:3:3,2:1:2,8:4:3,9:1:1"
|
||||
],
|
||||
"use_technique": true
|
||||
}
|
||||
],
|
||||
"battle_config": {
|
||||
"battle_id": 1,
|
||||
"stage_id": 1052106,
|
||||
"cycle_count": 30,
|
||||
"monster_wave": [
|
||||
[
|
||||
5035011
|
||||
]
|
||||
],
|
||||
"monster_level": 82,
|
||||
"blessings": []
|
||||
}
|
||||
}
|
||||
+19042
-5530
File diff suppressed because it is too large.
Load diff
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"CNBETAWin4.5.51": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16228654_7d9ae69e532c_ff87719729c35f",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16297504_50bd375a94d3_6512106296b202",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885",
|
||||
"ifix_version": "0",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16228766_9f45f1eb6490_9f172521b06e84",
|
||||
"lua_version": ""
|
||||
},
|
||||
"CNBETAWin4.5.52": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16317320_6a387bae4513_2f3e5644103a8a",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16353768_a1fcf7f6043c_508532c7de08bc",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885",
|
||||
"ifix_version": "0",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16317476_035e0a79224b_ed91499b6ef70b",
|
||||
"lua_version": ""
|
||||
},
|
||||
"CNBETAWin4.5.53": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16397599_dc132a900809_7e7a4d9792c13e",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16399740_f7f17473a4be_cd161900e78e53",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885",
|
||||
"ifix_version": "0",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16399471_bf17e3d5c3d7_5605fcd6b48271",
|
||||
"lua_version": ""
|
||||
},
|
||||
"CNBETAWin4.5.54": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16470326_3d7642f3b95a_1bb36b1c1b50ea",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16468722_9a3bb59a4d54_a6b44310dd6f23",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885",
|
||||
"ifix_version": "0",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16475505_fcd1e79987c2_dedcc3e216e164",
|
||||
"lua_version": ""
|
||||
},
|
||||
"CNBETAWin4.6.51": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16685262_bd0485299f5b_858f2db22344cc",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16706787_90982cd003eb_5ed57e0de4e2b4",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885",
|
||||
"ifix_version": "0",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16679499_5abbd35a74c5_a28e83e88b78d5",
|
||||
"lua_version": ""
|
||||
},
|
||||
"CNBETAWin4.6.52": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16745161_ab68020d7aa9_43440a8f18e051",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16755051_300cb1cd6a4f_c87db7a5d4f2bf",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885",
|
||||
"ifix_version": "0",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16745283_0b6f44e69f46_3bf68a1c7368cd",
|
||||
"lua_version": ""
|
||||
},
|
||||
"CNBETAWin4.6.53": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16818953_d4345bbd54a2_93c4aa5dfb978d",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16833202_959c316ef547_ac20bef3513343",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885",
|
||||
"ifix_version": "0",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16819203_679727d45bd7_91ccc9b73f52b7",
|
||||
"lua_version": ""
|
||||
}
|
||||
}
|
||||
@@ -182,6 +182,7 @@ type AvatarJson struct {
|
||||
SPValue *uint32 `json:"sp_value"`
|
||||
SPMax *uint32 `json:"sp_max"`
|
||||
EnhancedID *uint32 `json:"enhanced_id"`
|
||||
ExRankUnlocked bool `json:"ex_rank_unlocked,omitempty"`
|
||||
}
|
||||
|
||||
func (a *AvatarJson) SPCur() uint32 {
|
||||
@@ -305,6 +306,7 @@ type Persistent struct {
|
||||
EnableSWGlobal *bool `json:"enable_sw_global"`
|
||||
EnableCastoriceGlobal *bool `json:"enable_castorice_global"`
|
||||
DressedSkins map[uint32]uint32 `json:"dressed_skins"`
|
||||
ExRanksUnlocked map[uint32]bool `json:"ex_ranks_unlocked"`
|
||||
}
|
||||
|
||||
// DefaultPersistent mirrors the Rust Default impl.
|
||||
@@ -320,6 +322,7 @@ func DefaultPersistent() Persistent {
|
||||
EnableSWGlobal: &sw,
|
||||
EnableCastoriceGlobal: &cg,
|
||||
DressedSkins: map[uint32]uint32{},
|
||||
ExRanksUnlocked: map[uint32]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,6 +380,11 @@ func LoadFreesrData() (*FreesrData, error) {
|
||||
data.EnableSWGlobal = p.EnableSWGlobal
|
||||
data.EnableCastoriceGlobal = p.EnableCastoriceGlobal
|
||||
data.DressedSkins = p.DressedSkins
|
||||
for aid, unlocked := range p.ExRanksUnlocked {
|
||||
if av, ok := data.Avatars[aid]; ok {
|
||||
av.ExRankUnlocked = unlocked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if data.DressedSkins == nil {
|
||||
@@ -438,6 +446,12 @@ func (f *FreesrData) VerifyLineup() {
|
||||
|
||||
// SavePersistent writes the persistent subset back to the "persistent" file.
|
||||
func (f *FreesrData) SavePersistent() {
|
||||
exUnlocked := make(map[uint32]bool)
|
||||
for aid, av := range f.Avatars {
|
||||
if av.ExRankUnlocked {
|
||||
exUnlocked[aid] = true
|
||||
}
|
||||
}
|
||||
p := Persistent{
|
||||
Lineups: f.Lineups,
|
||||
Position: f.Position,
|
||||
@@ -447,6 +461,7 @@ func (f *FreesrData) SavePersistent() {
|
||||
EnableSWGlobal: f.EnableSWGlobal,
|
||||
EnableCastoriceGlobal: f.EnableCastoriceGlobal,
|
||||
DressedSkins: f.DressedSkins,
|
||||
ExRanksUnlocked: exUnlocked,
|
||||
}
|
||||
out, err := json.MarshalIndent(p, "", " ")
|
||||
if err != nil {
|
||||
@@ -477,3 +492,14 @@ func (f *FreesrData) FindRelicsFor(avatarID uint32) []*Relic {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// BaseAvatarIDs contains all playable base avatar IDs
|
||||
var BaseAvatarIDs = []uint32{
|
||||
8001, 1001,
|
||||
1002, 1003, 1004, 1005, 1006, 1008, 1009, 1013, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108,
|
||||
1109, 1110, 1111, 1112, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212,
|
||||
1213, 1214, 1215, 1217, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1312, 1315, 1310,
|
||||
1314, 1218, 1221, 1220, 1222, 1223, 1317, 1313, 1225, 1402, 1401, 1404, 1403, 1405, 1407, 1406,
|
||||
1409, 1014, 1015, 1408, 1410, 1412, 1413, 1414, 1415, 1321, 1501, 1502, 1503, 1504, 1505, 1506,
|
||||
1507, 1508, 1509, 1510, 1511, 1512, 1513,
|
||||
}
|
||||
+244
-6
@@ -94,11 +94,71 @@ var (
|
||||
teleportDB map[uint32]map[uint32]TeleportInfo
|
||||
)
|
||||
|
||||
// GameRes lazily parses res.json (13MB; parsed once).
|
||||
type MapEntranceEntry struct {
|
||||
ID uint32 `json:"ID"`
|
||||
PlaneID uint32 `json:"PlaneID"`
|
||||
FloorID uint32 `json:"FloorID"`
|
||||
StartGroupID uint32 `json:"StartGroupID"`
|
||||
StartAnchorID uint32 `json:"StartAnchorID"`
|
||||
}
|
||||
|
||||
type MazePlaneEntry struct {
|
||||
PlaneID uint32 `json:"PlaneID"`
|
||||
WorldID uint32 `json:"WorldID"`
|
||||
StartFloorID uint32 `json:"StartFloorID"`
|
||||
FloorIDList []uint32 `json:"FloorIDList"`
|
||||
}
|
||||
|
||||
type ResourceTeleport struct {
|
||||
TeleportID uint32 `json:"teleportId"`
|
||||
AnchorID uint32 `json:"anchorId"`
|
||||
GroupID uint32 `json:"groupId"`
|
||||
InstID uint32 `json:"instId"`
|
||||
Pos Vector `json:"pos"`
|
||||
Rot Vector `json:"rot"`
|
||||
}
|
||||
|
||||
type ResourceSceneEntry struct {
|
||||
PlaneID uint32 `json:"planeID"`
|
||||
EntryID uint32 `json:"entryID"`
|
||||
Props []ScenePropInfo `json:"props"`
|
||||
Monsters []SceneMonsterInfo `json:"monsters"`
|
||||
Teleports []ResourceTeleport `json:"teleports"`
|
||||
}
|
||||
|
||||
type ResourceResConfig struct {
|
||||
SceneConfig []ResourceSceneEntry `json:"scene_config"`
|
||||
}
|
||||
|
||||
type AnchorItem struct {
|
||||
ID uint32 `json:"ID"`
|
||||
Pos Vector `json:"pos"`
|
||||
Rot Vector `json:"rot"`
|
||||
}
|
||||
|
||||
type AnchorConfigEntry struct {
|
||||
EntryID uint32 `json:"entryID"`
|
||||
Anchor []AnchorItem `json:"anchor"`
|
||||
}
|
||||
|
||||
type AnchorConfigFile struct {
|
||||
AnchorConfig []AnchorConfigEntry `json:"anchor_config"`
|
||||
}
|
||||
|
||||
func readResourceFile(paths ...string) ([]byte, error) {
|
||||
for _, p := range paths {
|
||||
if raw, err := os.ReadFile(p); err == nil {
|
||||
return raw, nil
|
||||
}
|
||||
}
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
// GameRes lazily parses res.json and merges supplementary configs from resources/
|
||||
func GameRes() (*JsonConfig, error) {
|
||||
gameResOnce.Do(func() {
|
||||
gameRes = &JsonConfig{}
|
||||
raw, err := os.ReadFile("res.json")
|
||||
raw, err := readResourceFile("res.json", "resources/res.json")
|
||||
if err != nil {
|
||||
gameResErr = err
|
||||
return
|
||||
@@ -116,19 +176,197 @@ func GameRes() (*JsonConfig, error) {
|
||||
if gameRes.MapDefaultEntranceMap == nil {
|
||||
gameRes.MapDefaultEntranceMap = map[uint32]uint32{}
|
||||
}
|
||||
|
||||
_ = Teleports() // ensure teleportDB is initialized
|
||||
|
||||
loadSupplementaryResources(gameRes)
|
||||
})
|
||||
return gameRes, gameResErr
|
||||
}
|
||||
|
||||
func loadSupplementaryResources(res *JsonConfig) {
|
||||
entrancesByEntry := map[uint32]MapEntranceEntry{}
|
||||
if raw, err := readResourceFile("resources/MapEntrance.json", "MapEntrance.json"); err == nil {
|
||||
var list []MapEntranceEntry
|
||||
if json.Unmarshal(raw, &list) != nil {
|
||||
var m map[string][]MapEntranceEntry
|
||||
if json.Unmarshal(raw, &m) == nil {
|
||||
for _, entries := range m {
|
||||
list = append(list, entries...)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, e := range list {
|
||||
if e.FloorID != 0 && e.ID != 0 {
|
||||
if _, ok := res.MapDefaultEntranceMap[e.FloorID]; !ok {
|
||||
res.MapDefaultEntranceMap[e.FloorID] = e.ID
|
||||
}
|
||||
entrancesByEntry[e.ID] = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
planesByID := map[uint32]MazePlaneEntry{}
|
||||
if raw, err := readResourceFile("resources/MazePlane.json", "MazePlane.json"); err == nil {
|
||||
var list []MazePlaneEntry
|
||||
if json.Unmarshal(raw, &list) != nil {
|
||||
var m map[string][]MazePlaneEntry
|
||||
if json.Unmarshal(raw, &m) == nil {
|
||||
for _, entries := range m {
|
||||
list = append(list, entries...)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, p := range list {
|
||||
planesByID[p.PlaneID] = p
|
||||
if p.StartFloorID != 0 {
|
||||
if _, ok := res.MapDefaultEntranceMap[p.StartFloorID]; !ok {
|
||||
res.MapDefaultEntranceMap[p.StartFloorID] = p.PlaneID*100 + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if raw, err := readResourceFile("resources/res.json"); err == nil {
|
||||
var res2 ResourceResConfig
|
||||
if json.Unmarshal(raw, &res2) == nil && len(res2.SceneConfig) > 0 {
|
||||
for _, sc := range res2.SceneConfig {
|
||||
floorID := uint32(0)
|
||||
if ent, ok := entrancesByEntry[sc.EntryID]; ok && ent.FloorID != 0 {
|
||||
floorID = ent.FloorID
|
||||
} else if p, ok := planesByID[sc.PlaneID]; ok && p.StartFloorID != 0 {
|
||||
floorID = p.StartFloorID
|
||||
} else {
|
||||
floorID = sc.PlaneID*1000 + 1
|
||||
}
|
||||
|
||||
if _, ok := res.MapDefaultEntranceMap[floorID]; !ok {
|
||||
res.MapDefaultEntranceMap[floorID] = sc.EntryID
|
||||
}
|
||||
|
||||
if teleportDB[floorID] == nil {
|
||||
teleportDB[floorID] = map[uint32]TeleportInfo{}
|
||||
}
|
||||
for _, t := range sc.Teleports {
|
||||
tid := t.TeleportID
|
||||
if tid == 0 {
|
||||
tid = t.AnchorID
|
||||
}
|
||||
if tid != 0 {
|
||||
teleportDB[floorID][tid] = TeleportInfo{Pos: t.Pos, Rot: t.Rot}
|
||||
}
|
||||
}
|
||||
|
||||
if _, exists := res.LevelOutputConfigs[sc.EntryID]; !exists {
|
||||
groups := map[uint32]SceneData{}
|
||||
for _, p := range sc.Props {
|
||||
gid := p.GroupID
|
||||
if gid == 0 {
|
||||
gid = 1
|
||||
}
|
||||
sd := groups[gid]
|
||||
if sd.Teleports == nil {
|
||||
sd.Teleports = map[uint32]TeleportInfo{}
|
||||
}
|
||||
sd.Props = append(sd.Props, p)
|
||||
groups[gid] = sd
|
||||
}
|
||||
for _, m := range sc.Monsters {
|
||||
gid := m.GroupID
|
||||
if gid == 0 {
|
||||
gid = 1
|
||||
}
|
||||
sd := groups[gid]
|
||||
if sd.Teleports == nil {
|
||||
sd.Teleports = map[uint32]TeleportInfo{}
|
||||
}
|
||||
sd.Monsters = append(sd.Monsters, m)
|
||||
groups[gid] = sd
|
||||
}
|
||||
for _, t := range sc.Teleports {
|
||||
gid := t.GroupID
|
||||
if gid == 0 {
|
||||
gid = 1
|
||||
}
|
||||
sd := groups[gid]
|
||||
if sd.Teleports == nil {
|
||||
sd.Teleports = map[uint32]TeleportInfo{}
|
||||
}
|
||||
tid := t.TeleportID
|
||||
if tid == 0 {
|
||||
tid = t.AnchorID
|
||||
}
|
||||
if tid != 0 {
|
||||
sd.Teleports[tid] = TeleportInfo{Pos: t.Pos, Rot: t.Rot}
|
||||
}
|
||||
groups[gid] = sd
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("P%d_F%d", sc.PlaneID, floorID)
|
||||
res.LevelOutputConfigs[sc.EntryID] = map[string]*LevelOutputConfig{
|
||||
name: {
|
||||
IsEnteredSceneInfo: true,
|
||||
Scenes: groups,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if raw, err := readResourceFile("resources/Anchor.json", "Anchor.json"); err == nil {
|
||||
var aFile AnchorConfigFile
|
||||
if json.Unmarshal(raw, &aFile) == nil && len(aFile.AnchorConfig) > 0 {
|
||||
for _, item := range aFile.AnchorConfig {
|
||||
floorID := uint32(0)
|
||||
if ent, ok := entrancesByEntry[item.EntryID]; ok && ent.FloorID != 0 {
|
||||
floorID = ent.FloorID
|
||||
} else {
|
||||
floorID = item.EntryID*10 + 1
|
||||
}
|
||||
|
||||
if teleportDB[floorID] == nil {
|
||||
teleportDB[floorID] = map[uint32]TeleportInfo{}
|
||||
}
|
||||
for _, a := range item.Anchor {
|
||||
if a.ID != 0 {
|
||||
teleportDB[floorID][a.ID] = TeleportInfo{Pos: a.Pos, Rot: a.Rot}
|
||||
teleportDB[floorID][item.EntryID*100+a.ID] = TeleportInfo{Pos: a.Pos, Rot: a.Rot}
|
||||
}
|
||||
}
|
||||
|
||||
if _, exists := res.LevelOutputConfigs[item.EntryID]; !exists {
|
||||
tps := map[uint32]TeleportInfo{}
|
||||
for _, a := range item.Anchor {
|
||||
if a.ID != 0 {
|
||||
tps[a.ID] = TeleportInfo{Pos: a.Pos, Rot: a.Rot}
|
||||
tps[item.EntryID*100+a.ID] = TeleportInfo{Pos: a.Pos, Rot: a.Rot}
|
||||
}
|
||||
}
|
||||
name := fmt.Sprintf("P%d_F%d", item.EntryID/100, floorID)
|
||||
res.LevelOutputConfigs[item.EntryID] = map[string]*LevelOutputConfig{
|
||||
name: {
|
||||
IsEnteredSceneInfo: true,
|
||||
Scenes: map[uint32]SceneData{
|
||||
1: {Teleports: tps},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Teleports lazily parses teleports.json ({floor_id: {teleport_id: info}}).
|
||||
func Teleports() map[uint32]map[uint32]TeleportInfo {
|
||||
teleportOnce.Do(func() {
|
||||
teleportDB = map[uint32]map[uint32]TeleportInfo{}
|
||||
raw, err := os.ReadFile("teleports.json")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
raw, err := readResourceFile("teleports.json", "resources/teleports.json")
|
||||
if err == nil {
|
||||
_ = json.Unmarshal(raw, &teleportDB)
|
||||
}
|
||||
})
|
||||
return teleportDB
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"gac-hkrpg-go/internal/common"
|
||||
goproto "gac-hkrpg-go/protocol/proto"
|
||||
)
|
||||
|
||||
@@ -33,15 +34,7 @@ func protoMarshal(msg proto.Message) ([]byte, error) {
|
||||
// BASE_AVATAR_IDS — 89 avatars (Rust: handlers/avatar.rs)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var BaseAvatarIDs = []uint32{
|
||||
8001, 1001,
|
||||
1002, 1003, 1004, 1005, 1006, 1008, 1009, 1013, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108,
|
||||
1109, 1110, 1111, 1112, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212,
|
||||
1213, 1214, 1215, 1217, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1312, 1315, 1310,
|
||||
1314, 1218, 1221, 1220, 1222, 1223, 1317, 1313, 1225, 1402, 1401, 1404, 1403, 1405, 1407, 1406,
|
||||
1409, 1014, 1015, 1408, 1410, 1412, 1413, 1414, 1415, 1321, 1501, 1502, 1503, 1504, 1505, 1506,
|
||||
1507, 1508, 1509, 1510, 1511, 1512, 1513,
|
||||
}
|
||||
var BaseAvatarIDs = common.BaseAvatarIDs
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Dispatch — Go port of the trait_handler! macro + on_message special cases.
|
||||
@@ -109,7 +102,7 @@ func dummy(name string) {
|
||||
// never auto-answer these, drop them silently instead.
|
||||
var dangerFamilies = []string{
|
||||
"GridFight", "ChessRogue", "EvolveBuild", "ChimeraDuel",
|
||||
"Jukebox", "CycleScore", "MusicAlbum",
|
||||
"Jukebox", "CycleScore", "MusicAlbum", "DrinkMaker",
|
||||
}
|
||||
|
||||
func isDangerCmd(cmdID uint16) bool {
|
||||
@@ -121,7 +114,7 @@ func isDangerCmd(cmdID uint16) bool {
|
||||
}
|
||||
// CycleScore cmd ids (4143/4146) and GridFight range carry generic names
|
||||
switch cmdID {
|
||||
case 4143, 4146, 8541, 5410, 5449, 7142, 3174, 9252, 8175:
|
||||
case 4143, 4146, 8541, 5410, 5449, 7142, 3174, 9252, 8175, 6995, 6988:
|
||||
return true
|
||||
}
|
||||
// GetMapRotationData: an empty reply NREs the client's map init
|
||||
|
||||
@@ -183,12 +183,12 @@ func (g *Gateway) processKCPPayload(data []byte, addr *net.UDPAddr) {
|
||||
}
|
||||
}
|
||||
|
||||
// watchFreesrData polls freesr-data.json mtime every second (the Rust version
|
||||
// used a notify debouncer) and re-syncs the player on change.
|
||||
// watchFreesrData polls freesr-data.json mtime every second
|
||||
// and re-syncs the player in real-time when changed.
|
||||
func watchFreesrData(session *Session) {
|
||||
var lastMod time.Time
|
||||
var lastModFreesr time.Time
|
||||
if st, err := statFile("freesr-data.json"); err == nil {
|
||||
lastMod = st
|
||||
lastModFreesr = st
|
||||
}
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
@@ -197,18 +197,19 @@ func watchFreesrData(session *Session) {
|
||||
case <-session.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
st, err := statFile("freesr-data.json")
|
||||
if err != nil || !st.After(lastMod) {
|
||||
stFreesr, _ := statFile("freesr-data.json")
|
||||
if stFreesr.IsZero() || !stFreesr.After(lastModFreesr) {
|
||||
continue
|
||||
}
|
||||
lastMod = st
|
||||
lastModFreesr = stFreesr
|
||||
|
||||
if data, err := common.LoadFreesrData(); err == nil {
|
||||
session.SetJSON(data)
|
||||
if err := session.SyncPlayer(); err == nil {
|
||||
Log("json updated")
|
||||
Log("player data updated & live synced to client")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,12 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, id := range []uint32{1130301, 1100101, 1131001, 1141501, 1140701, 1150101, 1150501, 1140901} {
|
||||
if !seen[id] {
|
||||
seen[id] = true
|
||||
rsp.SkinList = append(rsp.SkinList, id)
|
||||
}
|
||||
}
|
||||
sort.Slice(rsp.SkinList, func(i, j int) bool { return rsp.SkinList[i] < rsp.SkinList[j] })
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,6 +2,7 @@ package gameserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
@@ -11,56 +12,88 @@ import (
|
||||
|
||||
func init() {
|
||||
handle("StartCocoonStage", func(s *Session, req *goproto.StartCocoonStageCsReq, rsp *goproto.StartCocoonStageScRsp) error {
|
||||
Log(fmt.Sprintf("[BATTLE] StartCocoonStage: CocoonId=%d Wave=%d WorldLevel=%d PropEntityId=%d", req.CocoonId, req.Wave, req.WorldLevel, req.PropEntityId))
|
||||
battleInfo := createBattleInfo(s, 0, 0)
|
||||
if req.WorldLevel > 0 {
|
||||
battleInfo.WorldLevel = req.WorldLevel
|
||||
}
|
||||
if req.Wave > 0 && int(req.Wave) <= len(battleInfo.MonsterWaveList) {
|
||||
battleInfo.MonsterWaveList = battleInfo.MonsterWaveList[:req.Wave]
|
||||
} else if len(battleInfo.MonsterWaveList) > 1 {
|
||||
battleInfo.MonsterWaveList = battleInfo.MonsterWaveList[:1]
|
||||
}
|
||||
battleInfo.JAOIOKBBMFC = uint32(len(battleInfo.MonsterWaveList))
|
||||
for _, w := range battleInfo.MonsterWaveList {
|
||||
w.WaveId = 1
|
||||
w.WaveParam = nil
|
||||
w.BattleStageId = battleInfo.StageId
|
||||
}
|
||||
normalizeCalyxBattleInfo(battleInfo, req.Wave, req.WorldLevel)
|
||||
CURRENT_BATTLE.Store(s.Token, battleInfo)
|
||||
|
||||
challengeCnt := req.Wave
|
||||
if challengeCnt == 0 {
|
||||
challengeCnt = 1
|
||||
}
|
||||
rsp.Retcode = 0
|
||||
rsp.PropEntityId = req.PropEntityId
|
||||
rsp.CocoonId = req.CocoonId
|
||||
rsp.Wave = req.Wave
|
||||
rsp.ChallengeCnt = challengeCnt
|
||||
rsp.BattleInfo = battleInfo
|
||||
return nil
|
||||
})
|
||||
|
||||
// NOTE: 4.6.51 proto names this message StartQuickCocoonStageCsReq/Rsp
|
||||
handleNamed("StartQuickCocoonStage", "StartQuickCocoonStageRsp", func(s *Session, req *goproto.StartQuickCocoonStageCsReq, rsp *goproto.StartQuickCocoonStageRsp) error {
|
||||
Log(fmt.Sprintf("[BATTLE] StartQuickCocoonStage: CocoonId=%d Wave=%d WorldLevel=%d", req.CocoonId, req.Wave, req.WorldLevel))
|
||||
battleInfo := createBattleInfo(s, 0, 0)
|
||||
normalizeCalyxBattleInfo(battleInfo, req.Wave, req.WorldLevel)
|
||||
CURRENT_BATTLE.Store(s.Token, battleInfo)
|
||||
|
||||
rsp.Retcode = 0
|
||||
rsp.CocoonId = req.CocoonId
|
||||
rsp.Wave = req.Wave
|
||||
rsp.BattleInfo = battleInfo
|
||||
return nil
|
||||
})
|
||||
|
||||
// NOTE: 4.6.51 proto names this message StartQuickCocoonStageCsReq/Rsp
|
||||
// (the Rust code referenced an older QuickStartCocoonStage* name).
|
||||
handleNamed("StartQuickCocoonStage", "StartQuickCocoonStageRsp", func(s *Session, req *goproto.StartQuickCocoonStageCsReq, rsp *goproto.StartQuickCocoonStageRsp) error {
|
||||
handleNamed("StartQuickFarmElement", "StartQuickFarmElementRsp", func(s *Session, req *goproto.StartQuickFarmElementCsReq, rsp *goproto.StartQuickFarmElementRsp) error {
|
||||
Log(fmt.Sprintf("[BATTLE] StartQuickFarmElement: FarmID=%d WorldLevel=%d", req.PAOFHFLFFHD, req.WorldLevel))
|
||||
battleInfo := createBattleInfo(s, 0, 0)
|
||||
if req.WorldLevel > 0 {
|
||||
battleInfo.WorldLevel = req.WorldLevel
|
||||
}
|
||||
if req.Wave > 0 && int(req.Wave) <= len(battleInfo.MonsterWaveList) {
|
||||
battleInfo.MonsterWaveList = battleInfo.MonsterWaveList[:req.Wave]
|
||||
} else if len(battleInfo.MonsterWaveList) > 1 {
|
||||
battleInfo.MonsterWaveList = battleInfo.MonsterWaveList[:1]
|
||||
}
|
||||
battleInfo.JAOIOKBBMFC = uint32(len(battleInfo.MonsterWaveList))
|
||||
for _, w := range battleInfo.MonsterWaveList {
|
||||
w.WaveId = 1
|
||||
w.WaveParam = nil
|
||||
w.BattleStageId = battleInfo.StageId
|
||||
}
|
||||
normalizeCalyxBattleInfo(battleInfo, 1, req.WorldLevel)
|
||||
CURRENT_BATTLE.Store(s.Token, battleInfo)
|
||||
|
||||
rsp.CocoonId = req.CocoonId
|
||||
rsp.Wave = req.Wave
|
||||
rsp.Retcode = 0
|
||||
rsp.PAOFHFLFFHD = req.PAOFHFLFFHD
|
||||
rsp.WorldLevel = req.WorldLevel
|
||||
rsp.BattleInfo = battleInfo
|
||||
return nil
|
||||
})
|
||||
|
||||
handle("ActiveFarmElement", func(s *Session, req *goproto.ActiveFarmElementCsReq, rsp *goproto.ActiveFarmElementScRsp) error {
|
||||
Log(fmt.Sprintf("[BATTLE] ActiveFarmElement: EntityId=%d WorldLevel=%d", req.EntityId, req.WorldLevel))
|
||||
rsp.Retcode = 0
|
||||
rsp.EntityId = req.EntityId
|
||||
rsp.WorldLevel = req.WorldLevel
|
||||
return nil
|
||||
})
|
||||
|
||||
handle("DeactivateFarmElement", func(s *Session, req *goproto.DeactivateFarmElementCsReq, rsp *goproto.DeactivateFarmElementScRsp) error {
|
||||
Log(fmt.Sprintf("[BATTLE] DeactivateFarmElement: EntityId=%d", req.EntityId))
|
||||
rsp.Retcode = 0
|
||||
rsp.EntityId = req.EntityId
|
||||
return nil
|
||||
})
|
||||
|
||||
handle("CocoonSweep", func(s *Session, req *goproto.CocoonSweepCsReq, rsp *goproto.CocoonSweepScRsp) error {
|
||||
Log(fmt.Sprintf("[BATTLE] CocoonSweep: CocoonId=%d WorldLevel=%d", req.CocoonId, req.WorldLevel))
|
||||
rsp.Retcode = 0
|
||||
rsp.CocoonId = req.CocoonId
|
||||
rsp.DropData = &goproto.ItemList{}
|
||||
rsp.MultipleDropData = &goproto.ItemList{}
|
||||
return nil
|
||||
})
|
||||
|
||||
handle("FarmElementSweep", func(s *Session, req *goproto.FarmElementSweepCsReq, rsp *goproto.FarmElementSweepScRsp) error {
|
||||
Log(fmt.Sprintf("[BATTLE] FarmElementSweep: FarmID=%d", req.PAOFHFLFFHD))
|
||||
rsp.Retcode = 0
|
||||
rsp.PAOFHFLFFHD = req.PAOFHFLFFHD
|
||||
rsp.MultipleDropData = &goproto.ItemList{}
|
||||
return nil
|
||||
})
|
||||
|
||||
handle("GetFarmStageGachaInfo", func(s *Session, req *goproto.GetFarmStageGachaInfoCsReq, rsp *goproto.GetFarmStageGachaInfoScRsp) error {
|
||||
rsp.Retcode = 0
|
||||
return nil
|
||||
})
|
||||
|
||||
handle("SceneEnterStage", func(s *Session, req *goproto.SceneEnterStageCsReq, rsp *goproto.SceneEnterStageScRsp) error {
|
||||
battleInfo := createBattleInfo(s, 0, 0)
|
||||
CURRENT_BATTLE.Store(s.Token, battleInfo)
|
||||
@@ -152,19 +185,32 @@ func init() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if player := s.JSON(); player != nil && len(player.BattleConfig.Monsters) > 0 {
|
||||
battleInfo := createBattleInfo(s, 0, 0)
|
||||
CURRENT_BATTLE.Store(s.Token, battleInfo)
|
||||
rsp.Retcode = 0
|
||||
rsp.BattleInfo = battleInfo
|
||||
return nil
|
||||
}
|
||||
|
||||
rsp.Retcode = 2000 // RET_BATTLE_STAGE_NOT_MATCH
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func normalizeCalyxBattleInfo(battleInfo *goproto.SceneBattleInfo, reqWave uint32, worldLevel uint32) {
|
||||
if worldLevel > 0 {
|
||||
battleInfo.WorldLevel = worldLevel
|
||||
}
|
||||
if reqWave > 0 && int(reqWave) <= len(battleInfo.MonsterWaveList) {
|
||||
battleInfo.MonsterWaveList = battleInfo.MonsterWaveList[:reqWave]
|
||||
} else if len(battleInfo.MonsterWaveList) > 1 {
|
||||
battleInfo.MonsterWaveList = battleInfo.MonsterWaveList[:1]
|
||||
}
|
||||
battleInfo.JAOIOKBBMFC = uint32(len(battleInfo.MonsterWaveList))
|
||||
for _, w := range battleInfo.MonsterWaveList {
|
||||
// NOTE: In client 4.6.53 GameAssembly.dll 0x166b2fdd, client verifies:
|
||||
// (wave.WaveId & ~8) == 1. If wave_id != 1, it jumps to 0x166b3161
|
||||
// and throws NullReferenceException in AdventureModule.CreateBattleServerCheckResult.
|
||||
// Therefore every wave in Calyx / FarmElement MUST have WaveId = 1 and WaveParam = nil.
|
||||
w.WaveId = 1
|
||||
w.WaveParam = nil
|
||||
w.BattleStageId = battleInfo.StageId
|
||||
}
|
||||
}
|
||||
|
||||
// createBattleInfo is the Go port of battle.rs create_battle_info.
|
||||
func createBattleInfo(s *Session, casterID, skillIndex uint32) *goproto.SceneBattleInfo {
|
||||
player := s.JSON()
|
||||
|
||||
@@ -62,8 +62,15 @@ func skipWireField(wireType uint32, buf *[]byte) {
|
||||
}
|
||||
|
||||
func mapChallengeStageID(id uint32) uint32 {
|
||||
cd := getChallengeData()
|
||||
if _, ok := cd.Challenges[id]; ok {
|
||||
return id
|
||||
}
|
||||
if _, ok := cd.Tierce[id]; ok {
|
||||
return id
|
||||
}
|
||||
switch {
|
||||
case id >= 5400 && id <= 5600: // MoC 1035/1036 -> 5301..5312
|
||||
case id >= 5400 && id <= 5700: // MoC 1035/1036 -> 5301..5312
|
||||
rem := id % 100
|
||||
if rem < 1 {
|
||||
rem = 1
|
||||
@@ -91,7 +98,6 @@ func mapChallengeStageID(id uint32) uint32 {
|
||||
}
|
||||
return 30200 + rem
|
||||
case id > 1:
|
||||
cd := getChallengeData()
|
||||
if _, ok := cd.Challenges[id]; !ok {
|
||||
return id - 1
|
||||
}
|
||||
@@ -100,10 +106,59 @@ func mapChallengeStageID(id uint32) uint32 {
|
||||
return id
|
||||
}
|
||||
|
||||
func readLenDelimAndAdvance(buf *[]byte) ([]byte, bool) {
|
||||
b := *buf
|
||||
l, n := readUvarint(b)
|
||||
if n == 0 || int(l) > len(b[n:]) {
|
||||
*buf = nil
|
||||
return nil, false
|
||||
}
|
||||
sub := b[n : n+int(l)]
|
||||
*buf = b[n+int(l):]
|
||||
return sub, true
|
||||
}
|
||||
|
||||
func readLenDelim(buf []byte) ([]byte, bool) {
|
||||
l, n := readUvarint(buf)
|
||||
if n == 0 || int(l) > len(buf[n:]) {
|
||||
return nil, false
|
||||
}
|
||||
return buf[n : n+int(l)], true
|
||||
}
|
||||
|
||||
func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
var req decodedChallengeStart
|
||||
buf := payload
|
||||
|
||||
var pbReq goproto.StartChallengeCsReq
|
||||
if err := proto.Unmarshal(payload, &pbReq); err == nil && pbReq.ChallengeId != 0 {
|
||||
req.challengeID = pbReq.ChallengeId
|
||||
req.firstAvatars = pbReq.FirstLineup
|
||||
if len(req.firstAvatars) == 0 && len(pbReq.AvatarLineupFirst) > 0 {
|
||||
for _, av := range pbReq.AvatarLineupFirst {
|
||||
if av.Id != 0 {
|
||||
req.firstAvatars = append(req.firstAvatars, av.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
req.secondAvatars = pbReq.SecondLineup
|
||||
if len(req.secondAvatars) == 0 && len(pbReq.AvatarLineupSecond) > 0 {
|
||||
for _, av := range pbReq.AvatarLineupSecond {
|
||||
if av.Id != 0 {
|
||||
req.secondAvatars = append(req.secondAvatars, av.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
if pbReq.StageInfo != nil {
|
||||
if pbReq.StageInfo.BossInfo != nil && pbReq.StageInfo.BossInfo.BuffOne != 0 {
|
||||
req.buffID = pbReq.StageInfo.BossInfo.BuffOne
|
||||
} else if pbReq.StageInfo.StoryInfo != nil && pbReq.StageInfo.StoryInfo.BuffOne != 0 {
|
||||
req.buffID = pbReq.StageInfo.StoryInfo.BuffOne
|
||||
}
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
buf := payload
|
||||
for len(buf) > 0 {
|
||||
tag, n := readUvarint(buf)
|
||||
if n == 0 {
|
||||
@@ -133,13 +188,10 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
buf = buf[n2:]
|
||||
req.firstAvatars = append(req.firstAvatars, uint32(v))
|
||||
case fieldNumber == 6 && wireType == 2:
|
||||
sub, ok := readLenDelim(buf)
|
||||
sub, ok := readLenDelimAndAdvance(&buf)
|
||||
if !ok {
|
||||
buf = nil
|
||||
break
|
||||
}
|
||||
buf = buf[len(buf)-len(sub):] // consumed below
|
||||
buf = buf[len(sub):]
|
||||
for len(sub) > 0 {
|
||||
v, n3 := readUvarint(sub)
|
||||
if n3 == 0 {
|
||||
@@ -157,12 +209,10 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
buf = buf[n2:]
|
||||
req.secondAvatars = append(req.secondAvatars, uint32(v))
|
||||
case fieldNumber == 8 && wireType == 2:
|
||||
sub, ok := readLenDelim(buf)
|
||||
sub, ok := readLenDelimAndAdvance(&buf)
|
||||
if !ok {
|
||||
buf = nil
|
||||
break
|
||||
}
|
||||
buf = buf[len(sub):]
|
||||
for len(sub) > 0 {
|
||||
v, n3 := readUvarint(sub)
|
||||
if n3 == 0 {
|
||||
@@ -171,14 +221,11 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
sub = sub[n3:]
|
||||
req.secondAvatars = append(req.secondAvatars, uint32(v))
|
||||
}
|
||||
case fieldNumber == 4 && wireType == 2:
|
||||
// AvatarLineup: id = tag 1
|
||||
sub, ok := readLenDelim(buf)
|
||||
case (fieldNumber == 4 || fieldNumber == 15) && wireType == 2:
|
||||
sub, ok := readLenDelimAndAdvance(&buf)
|
||||
if !ok {
|
||||
buf = nil
|
||||
break
|
||||
}
|
||||
buf = buf[len(sub):]
|
||||
for len(sub) > 0 {
|
||||
stag, n3 := readUvarint(sub)
|
||||
if n3 == 0 {
|
||||
@@ -192,19 +239,20 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
break
|
||||
}
|
||||
sub = sub[n3+n4:]
|
||||
if fieldNumber == 4 {
|
||||
req.firstAvatars = append(req.firstAvatars, uint32(v))
|
||||
} else {
|
||||
req.secondAvatars = append(req.secondAvatars, uint32(v))
|
||||
}
|
||||
} else {
|
||||
skipWireField(swt, &sub)
|
||||
}
|
||||
}
|
||||
case fieldNumber == 10 && wireType == 2:
|
||||
// ChallengeBuffInfo { story_info = 9, boss_info = 10 }
|
||||
sub, ok := readLenDelim(buf)
|
||||
sub, ok := readLenDelimAndAdvance(&buf)
|
||||
if !ok {
|
||||
buf = nil
|
||||
break
|
||||
}
|
||||
buf = buf[len(sub):]
|
||||
for len(sub) > 0 {
|
||||
stag, n3 := readUvarint(sub)
|
||||
if n3 == 0 {
|
||||
@@ -212,9 +260,9 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
}
|
||||
sfn := uint32(stag >> 3)
|
||||
swt := uint32(stag & 0x7)
|
||||
sub2 := sub[n3:]
|
||||
sub = sub[n3:]
|
||||
if swt == 2 && (sfn == 9 || sfn == 10) {
|
||||
inner, ok2 := readLenDelim(sub2)
|
||||
inner, ok2 := readLenDelimAndAdvance(&sub)
|
||||
if !ok2 {
|
||||
break
|
||||
}
|
||||
@@ -225,12 +273,13 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
}
|
||||
f2 := uint32(t2 >> 3)
|
||||
w2 := uint32(t2 & 0x7)
|
||||
inner = inner[n4:]
|
||||
if w2 == 0 && (f2 == 10 || f2 == 15) {
|
||||
v, n5 := readUvarint(inner[n4:])
|
||||
v, n5 := readUvarint(inner)
|
||||
if n5 == 0 {
|
||||
break
|
||||
}
|
||||
inner = inner[n4+n5:]
|
||||
inner = inner[n5:]
|
||||
if req.buffID == 0 {
|
||||
req.buffID = uint32(v)
|
||||
}
|
||||
@@ -238,14 +287,6 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
skipWireField(w2, &inner)
|
||||
}
|
||||
}
|
||||
sub = sub2
|
||||
// skip the whole len-delimited field
|
||||
_, n6 := readUvarint(sub2)
|
||||
if n6 == 0 {
|
||||
sub = nil
|
||||
} else {
|
||||
sub = sub2[n6:]
|
||||
}
|
||||
} else {
|
||||
skipWireField(swt, &sub)
|
||||
}
|
||||
@@ -258,13 +299,6 @@ func decodeStartChallengeReq(payload []byte) decodedChallengeStart {
|
||||
return req
|
||||
}
|
||||
|
||||
func readLenDelim(buf []byte) ([]byte, bool) {
|
||||
l, n := readUvarint(buf)
|
||||
if n == 0 || int(l) > len(buf[n:]) {
|
||||
return nil, false
|
||||
}
|
||||
return buf[n : n+int(l)], true
|
||||
}
|
||||
|
||||
type tierceStageEntry struct {
|
||||
buff uint32
|
||||
@@ -273,9 +307,27 @@ type tierceStageEntry struct {
|
||||
|
||||
func decodeStartChallengeTierceReq(payload []byte) decodedChallengeStart {
|
||||
var req decodedChallengeStart
|
||||
buf := payload
|
||||
var stagesAvatars []tierceStageEntry
|
||||
|
||||
var pbReq goproto.StartChallengeTierceCsReq
|
||||
if err := proto.Unmarshal(payload, &pbReq); err == nil && pbReq.ChallengeId != 0 {
|
||||
req.challengeID = pbReq.ChallengeId
|
||||
req.stageIndex = pbReq.FINOCEGDBDI
|
||||
req.isSingleStage = pbReq.MPKMIKBFEHG
|
||||
for _, lt := range pbReq.LineupTierceList {
|
||||
var l []uint32
|
||||
for _, av := range lt.TierceAvatarIdList {
|
||||
if av.Id != 0 {
|
||||
l = append(l, av.Id)
|
||||
}
|
||||
}
|
||||
stagesAvatars = append(stagesAvatars, tierceStageEntry{
|
||||
buff: lt.BuffId,
|
||||
lineup: l,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
buf := payload
|
||||
for len(buf) > 0 {
|
||||
tag, n := readUvarint(buf)
|
||||
if n == 0 {
|
||||
@@ -286,7 +338,7 @@ func decodeStartChallengeTierceReq(payload []byte) decodedChallengeStart {
|
||||
wireType := uint32(tag & 0x7)
|
||||
|
||||
switch {
|
||||
case (fieldNumber == 10 || fieldNumber == 1) && wireType == 0:
|
||||
case (fieldNumber == 10 || fieldNumber == 1 || fieldNumber == 5) && wireType == 0:
|
||||
v, n2 := readUvarint(buf)
|
||||
if n2 == 0 {
|
||||
buf = nil
|
||||
@@ -302,7 +354,7 @@ func decodeStartChallengeTierceReq(payload []byte) decodedChallengeStart {
|
||||
}
|
||||
buf = buf[n2:]
|
||||
req.challengeID = uint32(v)
|
||||
case (fieldNumber == 5 || fieldNumber == 13) && wireType == 0:
|
||||
case (fieldNumber == 2 || fieldNumber == 13) && wireType == 0:
|
||||
v, n2 := readUvarint(buf)
|
||||
if n2 == 0 {
|
||||
buf = nil
|
||||
@@ -310,13 +362,11 @@ func decodeStartChallengeTierceReq(payload []byte) decodedChallengeStart {
|
||||
}
|
||||
buf = buf[n2:]
|
||||
req.isSingleStage = v != 0
|
||||
case (fieldNumber == 12 || fieldNumber == 14) && wireType == 2:
|
||||
sub, ok := readLenDelim(buf)
|
||||
case (fieldNumber == 9 || fieldNumber == 12 || fieldNumber == 14) && wireType == 2:
|
||||
sub, ok := readLenDelimAndAdvance(&buf)
|
||||
if !ok {
|
||||
buf = nil
|
||||
break
|
||||
}
|
||||
buf = buf[len(sub):]
|
||||
|
||||
var stageBuff uint32
|
||||
var stageLineup []uint32
|
||||
@@ -327,22 +377,21 @@ func decodeStartChallengeTierceReq(payload []byte) decodedChallengeStart {
|
||||
}
|
||||
subFn := uint32(subTag >> 3)
|
||||
subWt := uint32(subTag & 0x7)
|
||||
sub = sub[n2:]
|
||||
switch {
|
||||
case (subFn == 6 || subFn == 5) && subWt == 0:
|
||||
v, n3 := readUvarint(sub[n2:])
|
||||
case (subFn == 10 || subFn == 6 || subFn == 5) && subWt == 0:
|
||||
v, n3 := readUvarint(sub)
|
||||
if n3 == 0 {
|
||||
sub = nil
|
||||
break
|
||||
}
|
||||
sub = sub[n2+n3:]
|
||||
sub = sub[n3:]
|
||||
stageBuff = uint32(v)
|
||||
case (subFn == 4 || subFn == 9) && subWt == 2:
|
||||
abuf, ok2 := readLenDelim(sub[n2:])
|
||||
case (subFn == 9 || subFn == 4) && subWt == 2:
|
||||
abuf, ok2 := readLenDelimAndAdvance(&sub)
|
||||
if !ok2 {
|
||||
sub = nil
|
||||
break
|
||||
}
|
||||
sub = sub[n2+len(abuf):]
|
||||
var aid uint32
|
||||
for len(abuf) > 0 {
|
||||
atag, n4 := readUvarint(abuf)
|
||||
@@ -351,12 +400,13 @@ func decodeStartChallengeTierceReq(payload []byte) decodedChallengeStart {
|
||||
}
|
||||
afn := uint32(atag >> 3)
|
||||
awt := uint32(atag & 0x7)
|
||||
if (afn == 7 || afn == 9) && awt == 0 {
|
||||
v, n5 := readUvarint(abuf[n4:])
|
||||
abuf = abuf[n4:]
|
||||
if (afn == 1 || afn == 7 || afn == 9) && awt == 0 {
|
||||
v, n5 := readUvarint(abuf)
|
||||
if n5 == 0 {
|
||||
break
|
||||
}
|
||||
abuf = abuf[n4+n5:]
|
||||
abuf = abuf[n5:]
|
||||
aid = uint32(v)
|
||||
} else {
|
||||
skipWireField(awt, &abuf)
|
||||
@@ -374,6 +424,7 @@ func decodeStartChallengeTierceReq(payload []byte) decodedChallengeStart {
|
||||
skipWireField(wireType, &buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, st := range stagesAvatars {
|
||||
req.stageInfoList = append(req.stageInfoList, st.lineup)
|
||||
@@ -636,7 +687,7 @@ func handleStartChallenge(s *Session, payload []byte) error {
|
||||
|
||||
baseID := mapChallengeStageID(req.challengeID)
|
||||
cd := getChallengeData()
|
||||
entrance, group, monster, event, buff := uint32(3014101), uint32(5), uint32(4033010), uint32(30124011), uint32(0)
|
||||
entrance, group, monster, event, buff := uint32(3000101), uint32(5), uint32(4033010), uint32(30124011), uint32(0)
|
||||
if c, ok := cd.Challenges[baseID]; ok {
|
||||
entrance, group, monster, event = c.Entrance, c.Group1, c.Monster1, c.Event1
|
||||
if req.buffID != 0 {
|
||||
@@ -644,13 +695,12 @@ func handleStartChallenge(s *Session, payload []byte) error {
|
||||
} else {
|
||||
buff = c.Buff
|
||||
}
|
||||
} else if t, ok := cd.Tierce[baseID]; ok {
|
||||
entrance, group, monster, event = t.Entrance, t.Group, t.Monster, t.Event
|
||||
buff = req.buffID
|
||||
}
|
||||
|
||||
// the 4.6.51 client has no MapEntrance 3014101 (Rust lesson: Lua
|
||||
// GotoMapParam nil -> stuck loading) even though res.json has it
|
||||
if entrance == 3014101 {
|
||||
entrance = 3000101
|
||||
} else if res, err := common.GameRes(); err == nil && !mapHasEntry(res, entrance) {
|
||||
if res, err := common.GameRes(); err == nil && !mapHasEntry(res, entrance) {
|
||||
Log("[CHALLENGE] Arena entrance missing in res.json, fallback to 3000101")
|
||||
entrance = 3000101
|
||||
}
|
||||
@@ -667,64 +717,37 @@ func handleStartChallenge(s *Session, payload []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
custom := map[uint32]uint32{}
|
||||
for i, aid := range chosenAvatars {
|
||||
custom[uint32(i)] = aid
|
||||
var lineups []*goproto.LineupInfo
|
||||
lineups = append(lineups, lineupInfoFromAvatars(chosenAvatars, sceneInfo.PlaneId, goproto.ExtraLineupType(1), 0))
|
||||
|
||||
secondAvatars := req.secondAvatars
|
||||
if len(secondAvatars) == 0 {
|
||||
secondAvatars = chosenAvatars
|
||||
}
|
||||
lineupInfo := common.ToLineupInfo(custom)
|
||||
secondAvatars = dedupeTruncate4(secondAvatars)
|
||||
lineups = append(lineups, lineupInfoFromAvatars(secondAvatars, sceneInfo.PlaneId, goproto.ExtraLineupType(1), 1))
|
||||
|
||||
curChallenge := &goproto.CurChallenge{
|
||||
ChallengeId: req.challengeID,
|
||||
Status: 1, // CHALLENGE_DOING
|
||||
Status: goproto.ChallengeStatus_CHALLENGE_DOING,
|
||||
RoundCount: 0,
|
||||
ScoreId: scoreIDFor(req.challengeID),
|
||||
ScoreTwo: 0,
|
||||
ExtraLineupType: goproto.ExtraLineupType(1),
|
||||
}
|
||||
|
||||
chalBuf, err := proto.Marshal(curChallenge)
|
||||
if err != nil {
|
||||
return err
|
||||
rsp := &goproto.StartChallengeScRsp{
|
||||
Retcode: 0,
|
||||
CurChallenge: curChallenge,
|
||||
LineupList: lineups,
|
||||
Scene: sceneInfo,
|
||||
}
|
||||
lineupBuf, err := proto.Marshal(lineupInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sceneBuf, err := proto.Marshal(sceneInfo)
|
||||
rspBuf, err := proto.Marshal(rsp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4.6.51: StartChallengeScRsp.stage_info (tag 13) - boss node info echoes
|
||||
// the selected buff so the client can set up the challenge UI
|
||||
stageInfo := &goproto.ChallengeStageInfo{}
|
||||
if req.buffID != 0 {
|
||||
stageInfo.BossInfo = &goproto.ChallengeBossInfo{
|
||||
FirstNode: &goproto.ChallengeBossSingleNodeInfo{BuffId: req.buffID},
|
||||
SecondNode: &goproto.ChallengeBossSingleNodeInfo{BuffId: req.buffID},
|
||||
}
|
||||
}
|
||||
stageBuf, err := proto.Marshal(stageInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var body []byte
|
||||
body = append(body, 0x18, 0x00) // tag 3: retcode = 0
|
||||
body = append(body, 0x2A) // tag 5: cur_challenge
|
||||
body = appendUvarint(body, uint64(len(chalBuf)))
|
||||
body = append(body, chalBuf...)
|
||||
body = append(body, 0x32) // tag 6: lineup_list
|
||||
body = appendUvarint(body, uint64(len(lineupBuf)))
|
||||
body = append(body, lineupBuf...)
|
||||
body = append(body, 0x52) // tag 10: scene
|
||||
body = appendUvarint(body, uint64(len(sceneBuf)))
|
||||
body = append(body, sceneBuf...)
|
||||
body = append(body, 0x6A) // tag 13: stage_info
|
||||
body = appendUvarint(body, uint64(len(stageBuf)))
|
||||
body = append(body, stageBuf...)
|
||||
|
||||
return s.SendRaw(&NetPacket{CmdType: 1787, Body: body})
|
||||
return s.SendRaw(&NetPacket{CmdType: 1787, Body: rspBuf})
|
||||
}
|
||||
|
||||
func scoreIDFor(challengeID uint32) uint32 {
|
||||
@@ -734,7 +757,7 @@ func scoreIDFor(challengeID uint32) uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// handleStartChallengeTierce ports handle_start_challenge_tierce -> cmd 8973.
|
||||
// handleStartChallengeTierce ports handle_start_challenge_tierce -> cmd 8998 (4.6.51).
|
||||
func handleStartChallengeTierce(s *Session, payload []byte) error {
|
||||
req := decodeStartChallengeTierceReq(payload)
|
||||
baseID := mapChallengeStageID(req.challengeID)
|
||||
@@ -751,9 +774,17 @@ func handleStartChallengeTierce(s *Session, payload []byte) error {
|
||||
case 0:
|
||||
chosenAvatars = req.firstAvatars
|
||||
case 1:
|
||||
if len(req.secondAvatars) > 0 {
|
||||
chosenAvatars = req.secondAvatars
|
||||
} else {
|
||||
chosenAvatars = req.firstAvatars
|
||||
}
|
||||
case 2:
|
||||
if len(req.thirdAvatars) > 0 {
|
||||
chosenAvatars = req.thirdAvatars
|
||||
} else {
|
||||
chosenAvatars = req.firstAvatars
|
||||
}
|
||||
}
|
||||
|
||||
if len(chosenAvatars) == 0 {
|
||||
@@ -768,32 +799,12 @@ func handleStartChallengeTierce(s *Session, payload []byte) error {
|
||||
}
|
||||
|
||||
cd := getChallengeData()
|
||||
entrance, group, monster, event, buff := uint32(3014101), uint32(5), uint32(4033010), uint32(30124011), uint32(0)
|
||||
switch {
|
||||
case req.stageIndex == 2:
|
||||
entrance, group, monster, event, buff := uint32(3000101), uint32(5), uint32(4033010), uint32(30124011), uint32(0)
|
||||
|
||||
if req.stageIndex == 0 {
|
||||
if t, ok := cd.Tierce[baseID]; ok {
|
||||
entrance, group, monster, event, buff = t.Entrance, t.Group, t.Monster, t.Event, req.buffID
|
||||
} else if c, ok2 := cd.Challenges[baseID]; ok2 {
|
||||
entrance, group, monster, event = c.Entrance2, c.Group2, c.Monster2, c.Event2
|
||||
if req.buffID != 0 {
|
||||
buff = req.buffID
|
||||
} else {
|
||||
buff = c.Buff
|
||||
}
|
||||
}
|
||||
case req.stageIndex == 1:
|
||||
if c, ok := cd.Challenges[baseID]; ok {
|
||||
entrance, group, monster, event = c.Entrance2, c.Group2, c.Monster2, c.Event2
|
||||
if req.buffID != 0 {
|
||||
buff = req.buffID
|
||||
} else {
|
||||
buff = c.Buff
|
||||
}
|
||||
} else {
|
||||
entrance, group, monster, event = 3014101, 6, 8013010, 30124012
|
||||
}
|
||||
default:
|
||||
if c, ok := cd.Challenges[baseID]; ok {
|
||||
entrance, group, monster, event = c.Entrance, c.Group1, c.Monster1, c.Event1
|
||||
if req.buffID != 0 {
|
||||
buff = req.buffID
|
||||
@@ -801,13 +812,32 @@ func handleStartChallengeTierce(s *Session, payload []byte) error {
|
||||
buff = c.Buff
|
||||
}
|
||||
}
|
||||
} else if req.stageIndex == 1 {
|
||||
// Stage 2 (Node 2)
|
||||
if c, ok := cd.Challenges[baseID-1]; ok && c.Entrance2 != 0 {
|
||||
entrance, group, monster, event = c.Entrance2, c.Group2, c.Monster2, c.Event2
|
||||
if req.buffID != 0 {
|
||||
buff = req.buffID
|
||||
} else {
|
||||
buff = c.Buff
|
||||
}
|
||||
} else if c, ok2 := cd.Challenges[baseID]; ok2 && c.Entrance2 != 0 {
|
||||
entrance, group, monster, event = c.Entrance2, c.Group2, c.Monster2, c.Event2
|
||||
if req.buffID != 0 {
|
||||
buff = req.buffID
|
||||
} else {
|
||||
buff = c.Buff
|
||||
}
|
||||
} else if t, ok3 := cd.Tierce[baseID]; ok3 {
|
||||
entrance, group, monster, event, buff = t.Entrance, t.Group, t.Monster, t.Event, req.buffID
|
||||
}
|
||||
} else {
|
||||
if t, ok := cd.Tierce[baseID]; ok {
|
||||
entrance, group, monster, event, buff = t.Entrance, t.Group, t.Monster, t.Event, req.buffID
|
||||
}
|
||||
}
|
||||
|
||||
// the 4.6.51 client cannot load arena 3014101 (no MapEntrance) - fall
|
||||
// back to the standard arena, mirroring handleStartChallenge
|
||||
if entrance == 3014101 {
|
||||
entrance = 3000101
|
||||
} else if res, err := common.GameRes(); err == nil && !mapHasEntry(res, entrance) {
|
||||
if res, err := common.GameRes(); err == nil && !mapHasEntry(res, entrance) {
|
||||
Log("[CHALLENGE] Tierce arena entrance missing in res.json, fallback to 3000101")
|
||||
entrance = 3000101
|
||||
}
|
||||
@@ -875,43 +905,27 @@ func handleStartChallengeTierce(s *Session, payload []byte) error {
|
||||
}
|
||||
}
|
||||
|
||||
// ChallengeTierceChallengeInfo: stage_index=4, challenge_id=6, is_single_stage=14, lineup_list=15
|
||||
var tierceInfo []byte
|
||||
tierceInfo = append(tierceInfo, 0x20) // tag 4: stage_index
|
||||
tierceInfo = appendUvarint(tierceInfo, uint64(req.stageIndex))
|
||||
tierceInfo = append(tierceInfo, 0x30) // tag 6: challenge_id
|
||||
tierceInfo = appendUvarint(tierceInfo, uint64(req.challengeID))
|
||||
if req.isSingleStage {
|
||||
tierceInfo = append(tierceInfo, 0x70, 0x01) // tag 14: true
|
||||
}
|
||||
|
||||
var tierceLineupList []*goproto.LineupInfo
|
||||
for stIdx, stageAvatars := range allLineups {
|
||||
lineup := lineupInfoFromAvatars(stageAvatars, sceneInfo.PlaneId, goproto.ExtraLineupType(1), uint32(stIdx))
|
||||
lineupBuf, err := proto.Marshal(lineup)
|
||||
tierceLineupList = append(tierceLineupList, lineupInfoFromAvatars(stageAvatars, sceneInfo.PlaneId, goproto.ExtraLineupType(1), uint32(stIdx)))
|
||||
}
|
||||
|
||||
// 4.6.51 StartChallengeTierceScRsp (CmdID 8998)
|
||||
rsp46 := &goproto.StartChallengeTierceScRsp{
|
||||
Retcode: 0,
|
||||
Scene: sceneInfo,
|
||||
CurTierceChallenge: &goproto.CurTierceChallenge{
|
||||
LineupList: tierceLineupList,
|
||||
ChallengeId: req.challengeID,
|
||||
FINOCEGDBDI: req.stageIndex,
|
||||
MPKMIKBFEHG: req.isSingleStage,
|
||||
},
|
||||
}
|
||||
rspBuf, err := proto.Marshal(rsp46)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tierceInfo = append(tierceInfo, 0x7A) // tag 15: lineup_list
|
||||
tierceInfo = appendUvarint(tierceInfo, uint64(len(lineupBuf)))
|
||||
tierceInfo = append(tierceInfo, lineupBuf...)
|
||||
}
|
||||
|
||||
sceneBuf, err := proto.Marshal(sceneInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// StartChallengeTierceScRsp cmd 8973: retcode=12, challenge_tierce_info=4, scene=9
|
||||
var body []byte
|
||||
body = append(body, 0x60, 0x00) // tag 12: retcode = 0
|
||||
body = append(body, 0x22) // tag 4
|
||||
body = appendUvarint(body, uint64(len(tierceInfo)))
|
||||
body = append(body, tierceInfo...)
|
||||
body = append(body, 0x4A) // tag 9: scene
|
||||
body = appendUvarint(body, uint64(len(sceneBuf)))
|
||||
body = append(body, sceneBuf...)
|
||||
|
||||
return s.SendRaw(&NetPacket{CmdType: 8973, Body: body})
|
||||
return s.SendRaw(&NetPacket{CmdType: 8998, Body: rspBuf})
|
||||
}
|
||||
|
||||
func handleSetChallengeTierceLineup(s *Session, payload []byte) error {
|
||||
@@ -921,34 +935,60 @@ func handleSetChallengeTierceLineup(s *Session, payload []byte) error {
|
||||
tierceLineups[challengeID] = stages
|
||||
tierceLineupsMu.Unlock()
|
||||
}
|
||||
if err := s.SendRaw(&NetPacket{CmdType: 8999, Body: []byte{0x08, 0x00}}); err != nil {
|
||||
// 4.6.51 SetChallengeTierceLineupScRsp (cmd 8985, retcode tag 1 = 0)
|
||||
if err := s.SendRaw(&NetPacket{CmdType: 8985, Body: []byte{0x08, 0x00}}); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = s.SendRaw(&NetPacket{CmdType: 8999, Body: []byte{0x08, 0x00}})
|
||||
_ = s.SendRaw(&NetPacket{CmdType: 8995, Body: []byte{0x60, 0x00}})
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleLeaveChallenge(s *Session, _ []byte) error {
|
||||
var returnEntry uint32 = 102020107
|
||||
if player := s.JSON(); player != nil {
|
||||
player.BattleConfig.CustomBattleLineup = nil
|
||||
// Always return safely to Belobog Admin District 102020107
|
||||
player.Scene.EntryID = 102020107
|
||||
player.Scene.PlaneID = 10202
|
||||
player.Scene.FloorID = 10202001
|
||||
player.Position = common.Position{X: -92, Y: 4278, Z: 68083, RotY: 155566}
|
||||
player.SavePersistent()
|
||||
}
|
||||
if err := s.SendRaw(&NetPacket{CmdType: 1716, Body: []byte{0x20, 0x00}}); err != nil {
|
||||
// 4.6.51 LeaveChallengeScRsp (cmd 1771, tag 10 retcode 0)
|
||||
if err := s.SendRaw(&NetPacket{CmdType: 1771, Body: []byte{0x50, 0x00}}); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = s.SendRaw(&NetPacket{CmdType: 1716, Body: []byte{0x20, 0x00}})
|
||||
_ = s.SendRaw(&NetPacket{CmdType: 1781, Body: []byte{0x40, 0x00}})
|
||||
|
||||
// Restore the player back to the open world
|
||||
_, _ = loadScene(s, returnEntry, true, nil, nil)
|
||||
_ = refreshLineup(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleLeaveChallengeTierce(s *Session, _ []byte) error {
|
||||
var returnEntry uint32 = 102020107
|
||||
if player := s.JSON(); player != nil {
|
||||
player.BattleConfig.CustomBattleLineup = nil
|
||||
// Always return safely to Belobog Admin District 102020107
|
||||
player.Scene.EntryID = 102020107
|
||||
player.Scene.PlaneID = 10202
|
||||
player.Scene.FloorID = 10202001
|
||||
player.Position = common.Position{X: -92, Y: 4278, Z: 68083, RotY: 155566}
|
||||
player.SavePersistent()
|
||||
}
|
||||
if err := s.SendRaw(&NetPacket{CmdType: 8997, Body: []byte{0x38, 0x00}}); err != nil {
|
||||
// 4.6.51 LeaveChallengeTierceScRsp (cmd 8983, tag 13 retcode 0)
|
||||
if err := s.SendRaw(&NetPacket{CmdType: 8983, Body: []byte{0x68, 0x00}}); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = s.SendRaw(&NetPacket{CmdType: 8997, Body: []byte{0x38, 0x00}})
|
||||
_ = s.SendRaw(&NetPacket{CmdType: 8982, Body: []byte{0x68, 0x00}})
|
||||
|
||||
// Restore the player back to the open world
|
||||
_, _ = loadScene(s, returnEntry, true, nil, nil)
|
||||
_ = refreshLineup(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1017,62 +1057,26 @@ func buildGetChallengeTierceDataScRspReal() []byte {
|
||||
}
|
||||
sort.Slice(tierceStages, func(i, j int) bool { return tierceStages[i].id < tierceStages[j].id })
|
||||
|
||||
var groups []*goproto.ChallengeTierceGroup
|
||||
for _, e := range tierceStages {
|
||||
groups = append(groups, &goproto.ChallengeTierceGroup{
|
||||
ChallengeId: e.id,
|
||||
IsWin: true,
|
||||
FinishedTargetList: e.targets,
|
||||
})
|
||||
}
|
||||
|
||||
rspMsg := &goproto.GetChallengeTierceDataScRsp{
|
||||
ChallengeTierceGroups: groups,
|
||||
Retcode: 0,
|
||||
}
|
||||
body, err := proto.Marshal(rspMsg)
|
||||
if err == nil {
|
||||
return body
|
||||
}
|
||||
|
||||
var rsp []byte
|
||||
rsp = append(rsp, 0x40, 0x00) // tag 8: retcode = 0
|
||||
rsp = append(rsp, 0x60, 0x00) // legacy tag 12: retcode = 0
|
||||
|
||||
for _, e := range tierceStages {
|
||||
var tierceData []byte
|
||||
|
||||
// tag 3: challenge_id
|
||||
tierceData = append(tierceData, 0x18)
|
||||
tierceData = appendUvarint(tierceData, uint64(e.id))
|
||||
// tag 6: is_passed = true
|
||||
tierceData = append(tierceData, 0x30, 0x01)
|
||||
// tag 7: finished_target_list (packed)
|
||||
var targetsBuf []byte
|
||||
for _, t := range e.targets {
|
||||
targetsBuf = appendUvarint(targetsBuf, uint64(t))
|
||||
}
|
||||
tierceData = append(tierceData, 0x3A)
|
||||
tierceData = appendUvarint(tierceData, uint64(len(targetsBuf)))
|
||||
tierceData = append(tierceData, targetsBuf...)
|
||||
|
||||
// tag 4: result_list (3 stages)
|
||||
for stageIdx := uint32(0); stageIdx < 3; stageIdx++ {
|
||||
var result []byte
|
||||
result = append(result, 0x18) // tag 3: score_id
|
||||
result = appendUvarint(result, uint64(e.score))
|
||||
result = append(result, 0x48) // tag 9: stage_index
|
||||
result = appendUvarint(result, uint64(stageIdx))
|
||||
result = append(result, 0x50, 0x01) // tag 10: end_status = WIN
|
||||
|
||||
tierceData = append(tierceData, 0x22)
|
||||
tierceData = appendUvarint(tierceData, uint64(len(result)))
|
||||
tierceData = append(tierceData, result...)
|
||||
}
|
||||
|
||||
// tag 8: stage_info_list (3 stages)
|
||||
for stageIdx := uint32(0); stageIdx < 3; stageIdx++ {
|
||||
var sinfo []byte
|
||||
sinfo = append(sinfo, 0x70) // tag 14: stage_index
|
||||
sinfo = appendUvarint(sinfo, uint64(stageIdx))
|
||||
|
||||
tierceData = append(tierceData, 0x42)
|
||||
tierceData = appendUvarint(tierceData, uint64(len(sinfo)))
|
||||
tierceData = append(tierceData, sinfo...)
|
||||
}
|
||||
|
||||
// tag 4: challenge_info_list (4.5.52)
|
||||
rsp = append(rsp, 0x22)
|
||||
rsp = appendUvarint(rsp, uint64(len(tierceData)))
|
||||
rsp = append(rsp, tierceData...)
|
||||
|
||||
// tag 10: challenge_info_list (legacy)
|
||||
rsp = append(rsp, 0x52)
|
||||
rsp = appendUvarint(rsp, uint64(len(tierceData)))
|
||||
rsp = append(rsp, tierceData...)
|
||||
}
|
||||
|
||||
return rsp
|
||||
}
|
||||
@@ -67,6 +67,29 @@ func loadChallengeScene(s *Session, entryID, targetGroupID, monsterID, eventID u
|
||||
}
|
||||
|
||||
// 2. position the player safely between boss and arena center
|
||||
// 2. detect arena floor platform height from props in scene
|
||||
// In some challenge arenas (e.g. entrance 3012602 / AS Starward 30225),
|
||||
// the boss monster pos in config is placed at Y = -193081 (22m below the floor),
|
||||
// while the arena stage prop (102259) is at Y = -170770.
|
||||
// Spawning at monsPos.Y puts the player under the map into the void.
|
||||
hasFloorProp := false
|
||||
var floorY int32 = -2147483648
|
||||
for _, g := range scene.Scenes {
|
||||
for _, p := range g.Props {
|
||||
dx := float64(p.Pos.X)
|
||||
dz := float64(p.Pos.Z)
|
||||
if math.Sqrt(dx*dx+dz*dz) < 150000.0 { // within 150 meters of center
|
||||
if p.Pos.Y > floorY {
|
||||
floorY = p.Pos.Y
|
||||
hasFloorProp = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if hasFloorProp && floorY > monsPos.Y {
|
||||
monsPos.Y = floorY
|
||||
}
|
||||
|
||||
centerVX := float64(-monsPos.X)
|
||||
centerVZ := float64(-monsPos.Z)
|
||||
distToCenter := math.Sqrt(centerVX*centerVX + centerVZ*centerVZ)
|
||||
@@ -99,10 +122,7 @@ func loadChallengeScene(s *Session, entryID, targetGroupID, monsterID, eventID u
|
||||
Pos: &goproto.Vector{X: spawnX, Y: spawnY, Z: spawnZ},
|
||||
Rot: &goproto.Vector{Y: spawnRotY},
|
||||
}
|
||||
|
||||
player.Scene.EntryID = entryID
|
||||
player.Scene.FloorID = floorID
|
||||
player.Scene.PlaneID = planeID
|
||||
// NOTE: keep player.Scene intact so returning to overworld or relogging spawns in overworld
|
||||
|
||||
sceneInfo := &goproto.SceneInfo{
|
||||
FloorId: floorID,
|
||||
@@ -119,13 +139,10 @@ func loadChallengeScene(s *Session, entryID, targetGroupID, monsterID, eventID u
|
||||
}
|
||||
sceneInfo.LightenSectionList = scene.Sections
|
||||
|
||||
// load props for group 1, target group, and common prop groups (7/8)
|
||||
// load all props in the arena scene (do not filter out prop groups like group 2 / group 3)
|
||||
propEntityID := uint32(1000)
|
||||
s.PropEntities = map[uint32]PropEntry{}
|
||||
for _, gid := range sortedU32Keys2(scene.Scenes) {
|
||||
if gid != 1 && gid != targetGroupID && gid != 7 && gid != 8 {
|
||||
continue
|
||||
}
|
||||
group := scene.Scenes[gid]
|
||||
groupInfo := &goproto.SceneEntityGroupInfo{GroupId: gid}
|
||||
for i := range group.Props {
|
||||
|
||||
@@ -50,9 +50,17 @@ func init() {
|
||||
rsp.RelicList = append(rsp.RelicList, player.Relics[i].ToProtoRelic())
|
||||
}
|
||||
rsp.MaterialList = []*goproto.Material{
|
||||
{Tid: 1, Num: 9999999}, // Stellar Jade
|
||||
{Tid: 2, Num: 99999999}, // Credit
|
||||
{Tid: 101, Num: 999999}, // Normal Pass
|
||||
{Tid: 102, Num: 999999}, // Special Pass
|
||||
}
|
||||
for _, aid := range BaseAvatarIDs {
|
||||
rsp.MaterialList = append(rsp.MaterialList, &goproto.Material{
|
||||
Tid: 130000 + aid,
|
||||
Num: 10,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -99,7 +107,30 @@ func init() {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
isExRank := false
|
||||
if req.CostData != nil {
|
||||
for _, item := range req.CostData.ItemList {
|
||||
if pile := item.GetPileItem(); pile != nil {
|
||||
if (pile.ItemId >= 130000 && pile.ItemId < 140000) || pile.ItemId == 131204 || pile.ItemId == 131213 || pile.ItemId == 131308 {
|
||||
isExRank = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if req.Rank > 6 {
|
||||
isExRank = true
|
||||
}
|
||||
|
||||
if isExRank || (avatar.Data.Rank >= 6 && common.AvatarExRankMap[req.AvatarId] != 0) {
|
||||
avatar.ExRankUnlocked = true
|
||||
if req.Rank > 0 && req.Rank <= 6 {
|
||||
avatar.Data.Rank = req.Rank
|
||||
}
|
||||
} else {
|
||||
avatar.Data.Rank = req.Rank
|
||||
}
|
||||
|
||||
ret := &goproto.PlayerSyncScNotify{}
|
||||
buildSync(player, ret, []uint32{avatar.AvatarID}, nil, nil)
|
||||
|
||||
@@ -2,6 +2,9 @@ package gameserver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
goproto "gac-hkrpg-go/protocol/proto"
|
||||
)
|
||||
@@ -37,7 +40,23 @@ func init() {
|
||||
|
||||
var finalData []byte
|
||||
finalData = append(finalData, heartbeatLua...)
|
||||
if freecam, err := os.ReadFile("scripts/freecam.lua"); err == nil {
|
||||
|
||||
// Load all .lua files from scripts/ directory in deterministic order
|
||||
if entries, err := os.ReadDir("scripts"); err == nil {
|
||||
var luaFiles []string
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".lua") {
|
||||
luaFiles = append(luaFiles, entry.Name())
|
||||
}
|
||||
}
|
||||
sort.Strings(luaFiles)
|
||||
for _, name := range luaFiles {
|
||||
if content, err := os.ReadFile(filepath.Join("scripts", name)); err == nil {
|
||||
finalData = append(finalData, '\n')
|
||||
finalData = append(finalData, content...)
|
||||
}
|
||||
}
|
||||
} else if freecam, err := os.ReadFile("scripts/freecam.lua"); err == nil {
|
||||
finalData = append(finalData, '\n')
|
||||
finalData = append(finalData, freecam...)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package gameserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"gac-hkrpg-go/internal/common"
|
||||
goproto "gac-hkrpg-go/protocol/proto"
|
||||
)
|
||||
|
||||
@@ -57,6 +62,7 @@ func init() {
|
||||
simpleReply([]uint16{2966}, 2955, []byte{0x30, 0x00}) // tag 6 retcode 0
|
||||
simpleReply([]uint16{2916}, 2906, []byte{0x18, 0x00}) // tag 3 retcode 0
|
||||
simpleReply([]uint16{2919}, 2928, []byte{0x48, 0x00}) // tag 9 retcode 0
|
||||
simpleReply([]uint16{2974}, 2919, []byte{0x18, 0x00}) // GetFriendListInfo tag 3 retcode 0
|
||||
simpleReply([]uint16{740}, 733, []byte{0x58, 0x00}) // tag 11 retcode 0
|
||||
simpleReply([]uint16{1748}, 1745, []byte{0x08, 0x00}) // tag 1 retcode 0
|
||||
simpleReply([]uint16{1765}, 1730, []byte{0x08, 0x00}) // tag 1 retcode 0
|
||||
@@ -64,7 +70,7 @@ func init() {
|
||||
simpleReply([]uint16{1738}, 1748, nil) // empty body
|
||||
simpleReply([]uint16{188}, 181, []byte{0x50, 0x00}) // tag 10 retcode 0
|
||||
simpleReply([]uint16{4130}, 4124, []byte{0x70, 0x00}) // tag 14 retcode 0
|
||||
simpleReply([]uint16{8110, 8115}, 8104, []byte{0x18, 0x00}) // GetSwitchHandData tag 3 retcode 0
|
||||
simpleReply([]uint16{8110, 8115}, 8108, []byte{0x18, 0x00}) // GetSwitchHandData tag 3 retcode 0 (4.6.51 CmdID: 8108)
|
||||
simpleReply([]uint16{8993, 8991}, 8981, []byte{0x70, 0x00}) // 4.6.51: tag 14 retcode 0
|
||||
simpleReply([]uint16{8987}, 8986, []byte{0x38, 0x00}) // ConfirmChallengeTierceStageSettle tag 7 retcode 0
|
||||
|
||||
@@ -81,21 +87,98 @@ func init() {
|
||||
rawHandlers[1731] = handleStartChallenge
|
||||
rawHandlers[1793] = handleStartChallenge
|
||||
rawHandlers[8986] = handleStartChallengeTierce
|
||||
rawHandlers[8983] = handleStartChallengeTierce
|
||||
rawHandlers[8988] = handleStartChallengeTierce
|
||||
rawHandlers[8978] = handleSetChallengeTierceLineup
|
||||
rawHandlers[9000] = handleSetChallengeTierceLineup
|
||||
rawHandlers[1705] = handleLeaveChallenge
|
||||
rawHandlers[1760] = handleLeaveChallenge
|
||||
rawHandlers[1788] = handleLeaveChallenge
|
||||
rawHandlers[8989] = handleLeaveChallengeTierce
|
||||
rawHandlers[8998] = handleLeaveChallengeTierce
|
||||
rawHandlers[1739] = handleTakeChallengeReward
|
||||
|
||||
// ---- 400 -> Ex-Rank / Affinitive Eidolon Unlock ----
|
||||
rawHandlers[400] = handleExRankUnlock
|
||||
}
|
||||
|
||||
// challengeGroupStatsReply ports the 1775/1711 arm: pull group_id (field 12
|
||||
// varint) from the request and answer cmd 1708 with per-mode statistics.
|
||||
// handleExRankUnlock handles CmdID 400 (Affinitive Eidolon / Ex-Rank unlock request from client).
|
||||
func handleExRankUnlock(s *Session, payload []byte) error {
|
||||
Log(fmt.Sprintf("[EX-RANK] Received cmd 400 (len: %d) payload: %x", len(payload), payload))
|
||||
|
||||
var parsedVal uint32
|
||||
buf := payload
|
||||
for len(buf) > 0 {
|
||||
tag, n := readUvarint(buf)
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
buf = buf[n:]
|
||||
wireType := tag & 0x7
|
||||
fieldNum := tag >> 3
|
||||
if wireType == 0 {
|
||||
v, n2 := readUvarint(buf)
|
||||
if n2 > 0 {
|
||||
buf = buf[n2:]
|
||||
Log(fmt.Sprintf("[EX-RANK] field %d = %d", fieldNum, v))
|
||||
if v >= 100000 || (v >= 1000 && v < 10000 && parsedVal == 0) {
|
||||
parsedVal = uint32(v)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
skipWireField(uint32(wireType), &buf)
|
||||
}
|
||||
|
||||
var avatarID uint32
|
||||
if parsedVal >= 100000 {
|
||||
avatarID = parsedVal / 100 // e.g. 120401 -> 1204, 121301 -> 1213, 130801 -> 1308
|
||||
} else if parsedVal >= 1000 && parsedVal < 10000 {
|
||||
avatarID = parsedVal
|
||||
}
|
||||
|
||||
Log(fmt.Sprintf("[EX-RANK] Target avatarID: %d (from val: %d)", avatarID, parsedVal))
|
||||
|
||||
player := s.JSON()
|
||||
if player != nil {
|
||||
if avatarID == 0 {
|
||||
// Fallback: unlock for all avatars that have ExRank defined (1204, 1213, 1308)
|
||||
for aid := range common.AvatarExRankMap {
|
||||
if av, ok := player.Avatars[aid]; ok {
|
||||
av.ExRankUnlocked = true
|
||||
Log(fmt.Sprintf("[EX-RANK] Fallback set ExRankUnlocked=true for avatar %d", aid))
|
||||
syncNotify := &goproto.PlayerSyncScNotify{}
|
||||
buildSync(player, syncNotify, []uint32{aid}, nil, nil)
|
||||
_ = s.Send(syncNotify)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if av, ok := player.Avatars[avatarID]; ok {
|
||||
av.ExRankUnlocked = true
|
||||
Log(fmt.Sprintf("[EX-RANK] Set ExRankUnlocked=true for avatar %d", avatarID))
|
||||
}
|
||||
syncNotify := &goproto.PlayerSyncScNotify{}
|
||||
buildSync(player, syncNotify, []uint32{avatarID}, nil, nil)
|
||||
_ = s.Send(syncNotify)
|
||||
}
|
||||
player.SavePersistent()
|
||||
}
|
||||
|
||||
// Send candidate response packets so client RPC callback resolves without spinning
|
||||
// Candidates in CmdAvatarType: 395 (CmdAvatarType_ENKEIJOLCNO), 354, 355, 364, 341
|
||||
for _, rspID := range []uint16{395, 354, 355, 364, 341} {
|
||||
_ = s.SendRaw(&NetPacket{CmdType: rspID, Body: []byte{}})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// challengeGroupStatsReply handles CmdGetChallengeGroupStatisticsCsReq (1775/1711)
|
||||
// and returns GetChallengeGroupStatisticsScRsp (1708) using standard 4.6.51 proto.
|
||||
func challengeGroupStatsReply(s *Session, payload []byte) error {
|
||||
groupID := uint32(100)
|
||||
var req goproto.GetChallengeGroupStatisticsCsReq
|
||||
if err := proto.Unmarshal(payload, &req); err == nil && req.GroupId != 0 {
|
||||
groupID = req.GroupId
|
||||
} else {
|
||||
buf := payload
|
||||
for len(buf) > 0 {
|
||||
tag, n := readUvarint(buf)
|
||||
@@ -105,89 +188,55 @@ func challengeGroupStatsReply(s *Session, payload []byte) error {
|
||||
buf = buf[n:]
|
||||
fieldNumber := tag >> 3
|
||||
wireType := tag & 0x7
|
||||
if fieldNumber == 12 && wireType == 0 {
|
||||
if (fieldNumber == 5 || fieldNumber == 12) && wireType == 0 {
|
||||
v, n2 := readUvarint(buf)
|
||||
if n2 == 0 {
|
||||
break
|
||||
}
|
||||
buf = buf[n2:]
|
||||
if n2 > 0 {
|
||||
groupID = uint32(v)
|
||||
break
|
||||
} else if wireType == 0 {
|
||||
_, n2 := readUvarint(buf)
|
||||
if n2 == 0 {
|
||||
break
|
||||
}
|
||||
buf = buf[n2:]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
skipWireField(uint32(wireType), &buf)
|
||||
}
|
||||
}
|
||||
|
||||
var body []byte
|
||||
// tag 9: group_id
|
||||
body = append(body, 0x48)
|
||||
body = appendUvarint(body, uint64(groupID))
|
||||
// tag 15: retcode 0
|
||||
body = append(body, 0x78, 0x00)
|
||||
rsp := &goproto.GetChallengeGroupStatisticsScRsp{
|
||||
GroupId: groupID,
|
||||
Retcode: 0,
|
||||
}
|
||||
|
||||
switch {
|
||||
case groupID >= 2000 && groupID < 3000:
|
||||
// Pure Fiction: tag 8 = challenge_story (ChallengeStoryStatistics)
|
||||
var eik []byte
|
||||
eik = append(eik, 0x50) // tag 10: score_id 80000
|
||||
eik = appendUvarint(eik, 80000)
|
||||
eik = append(eik, 0x58) // tag 11: level 4
|
||||
eik = appendUvarint(eik, 4)
|
||||
|
||||
var css []byte
|
||||
css = append(css, 0x32) // tag 6
|
||||
css = appendUvarint(css, uint64(len(eik)))
|
||||
css = append(css, eik...)
|
||||
css = append(css, 0x38) // tag 7: record_id 1
|
||||
css = appendUvarint(css, 1)
|
||||
|
||||
body = append(body, 0x42) // tag 8
|
||||
body = appendUvarint(body, uint64(len(css)))
|
||||
body = append(body, css...)
|
||||
case groupID >= 1000 && groupID < 2000:
|
||||
// Memory of Chaos: tag 2 = challenge_default (ChallengeStatistics)
|
||||
var adk []byte
|
||||
adk = append(adk, 0x10) // tag 2: round_count 20
|
||||
adk = appendUvarint(adk, 20)
|
||||
adk = append(adk, 0x18) // tag 3: level 12
|
||||
adk = appendUvarint(adk, 12)
|
||||
|
||||
var cs []byte
|
||||
cs = append(cs, 0x32) // tag 6
|
||||
cs = appendUvarint(cs, uint64(len(adk)))
|
||||
cs = append(cs, adk...)
|
||||
cs = append(cs, 0x50) // tag 10: record_id 1
|
||||
cs = appendUvarint(cs, 1)
|
||||
|
||||
body = append(body, 0x12) // tag 2
|
||||
body = appendUvarint(body, uint64(len(cs)))
|
||||
body = append(body, cs...)
|
||||
case groupID >= 3000:
|
||||
// Apocalyptic Shadow: tag 11 = challenge_boss (ChallengeBossStatistics)
|
||||
var aan []byte
|
||||
aan = append(aan, 0x50) // tag 10: score_id 8000
|
||||
aan = appendUvarint(aan, 8000)
|
||||
aan = append(aan, 0x18) // tag 3: level 4
|
||||
aan = appendUvarint(aan, 4)
|
||||
|
||||
var cbs []byte
|
||||
cbs = append(cbs, 0x0A) // tag 1
|
||||
cbs = appendUvarint(cbs, uint64(len(aan)))
|
||||
cbs = append(cbs, aan...)
|
||||
cbs = append(cbs, 0x70) // tag 14: record_id 1
|
||||
cbs = appendUvarint(cbs, 1)
|
||||
|
||||
body = append(body, 0x5A) // tag 11
|
||||
body = appendUvarint(body, uint64(len(cbs)))
|
||||
body = append(body, cbs...)
|
||||
// Apocalyptic Shadow: challenge_boss (field 1)
|
||||
rsp.ChallengeBoss = &goproto.ChallengeBossStatistics{
|
||||
RecordId: 1,
|
||||
PPBHLLOJNEK: &goproto.AANLJBLOOFO{
|
||||
Level: 4,
|
||||
ScoreId: 8000,
|
||||
},
|
||||
}
|
||||
case groupID >= 2000 && groupID < 3000:
|
||||
// Pure Fiction: challenge_story (field 3)
|
||||
rsp.ChallengeStory = &goproto.ChallengeStoryStatistics{
|
||||
RecordId: 1,
|
||||
PPBHLLOJNEK: &goproto.EIKPHEMHIOH{
|
||||
ScoreId: 80000,
|
||||
},
|
||||
}
|
||||
case groupID >= 1000 && groupID < 2000:
|
||||
// Memory of Chaos: challenge_default (field 11)
|
||||
rsp.ChallengeDefault = &goproto.ChallengeStatistics{
|
||||
RecordId: 1,
|
||||
PPBHLLOJNEK: &goproto.ADKJKMKBFDC{
|
||||
RoundCount: 20,
|
||||
Level: 12,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
body, err := proto.Marshal(rsp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.SendRaw(&NetPacket{CmdType: 1708, Body: body})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,16 +20,36 @@ import (
|
||||
// Rust iterated a HashMap arbitrarily; we pick the lexicographically first
|
||||
// name for determinism.
|
||||
func firstSceneOfEntry(res *common.JsonConfig, entryID uint32) (string, *common.LevelOutputConfig, bool) {
|
||||
m, ok := res.LevelOutputConfigs[entryID]
|
||||
if !ok || len(m) == 0 {
|
||||
return "", nil, false
|
||||
}
|
||||
if m, ok := res.LevelOutputConfigs[entryID]; ok && len(m) > 0 {
|
||||
names := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
names = append(names, k)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names[0], m[names[0]], true
|
||||
}
|
||||
if entryID != 0 {
|
||||
name := fmt.Sprintf("P%d_F%d", entryID/100, entryID*10+1)
|
||||
cfg := &common.LevelOutputConfig{
|
||||
IsEnteredSceneInfo: true,
|
||||
Scenes: map[uint32]common.SceneData{
|
||||
1: {
|
||||
Teleports: map[uint32]common.TeleportInfo{
|
||||
entryID: {Pos: common.Vector{X: 0, Y: 0, Z: 0}},
|
||||
1: {Pos: common.Vector{X: 0, Y: 0, Z: 0}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if res.LevelOutputConfigs == nil {
|
||||
res.LevelOutputConfigs = map[uint32]map[string]*common.LevelOutputConfig{}
|
||||
}
|
||||
res.LevelOutputConfigs[entryID] = map[string]*common.LevelOutputConfig{
|
||||
name: cfg,
|
||||
}
|
||||
return name, cfg, true
|
||||
}
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
// parsePlaneFloor splits "P<plane>_F<floor>".
|
||||
@@ -65,19 +85,24 @@ func init() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// recover from a stale challenge arena back to the Parlor Car
|
||||
// recover from a stale challenge arena, broken map, or void coordinate back to Belobog Admin District
|
||||
entryID := player.Scene.EntryID
|
||||
if entryID >= 3000000 && entryID < 4000000 {
|
||||
entryID = 100000104
|
||||
if (entryID >= 3000000 && entryID < 4000000) || entryID == 100000104 || entryID == 100000304 || entryID == 1030402 || entryID == 0 || player.Position.Y < -50000 {
|
||||
entryID = 102020107
|
||||
player.Scene.EntryID = 102020107
|
||||
player.Scene.PlaneID = 10202
|
||||
player.Scene.FloorID = 10202001
|
||||
player.Position = common.Position{X: -92, Y: 4278, Z: 68083, RotY: 155566}
|
||||
player.SavePersistent()
|
||||
}
|
||||
|
||||
defaultScene := &goproto.SceneInfo{
|
||||
GameModeType: 1,
|
||||
EntryId: entryID,
|
||||
}
|
||||
if entryID == 100000104 {
|
||||
defaultScene.PlaneId = 10000
|
||||
defaultScene.FloorId = 10000000
|
||||
if entryID == 102020107 {
|
||||
defaultScene.PlaneId = 10202
|
||||
defaultScene.FloorId = 10202001
|
||||
} else {
|
||||
defaultScene.PlaneId = player.Scene.PlaneID
|
||||
defaultScene.FloorId = player.Scene.FloorID
|
||||
@@ -103,10 +128,10 @@ func init() {
|
||||
}
|
||||
player := s.JSON()
|
||||
currentEntry := func() uint32 {
|
||||
if player != nil {
|
||||
if player != nil && player.Scene.EntryID != 0 && player.Scene.EntryID != 100000104 && player.Scene.EntryID != 100000304 {
|
||||
return player.Scene.EntryID
|
||||
}
|
||||
return 100000104
|
||||
return 102020107
|
||||
}
|
||||
|
||||
interactID := uint32(req.InteractId)
|
||||
@@ -115,17 +140,17 @@ func init() {
|
||||
sceneIdent = proto.Clone(req.SceneIdentifier).(*goproto.SceneIdentifier)
|
||||
}
|
||||
|
||||
// resolve target entry (mirrors the Rust resolution chain)
|
||||
// resolve target entry (mirrors the Rust resolution chain, with cascading fallbacks)
|
||||
resolvedEntry := uint32(0)
|
||||
switch {
|
||||
case req.EntryId != 0 && mapHasEntry(res, req.EntryId):
|
||||
if req.EntryId != 0 && mapHasEntry(res, req.EntryId) {
|
||||
resolvedEntry = req.EntryId
|
||||
case sceneIdent != nil && sceneIdent.FloorId != 0:
|
||||
} else if sceneIdent != nil && sceneIdent.FloorId != 0 {
|
||||
resolvedEntry = findEntryForFloor(res, sceneIdent.FloorId)
|
||||
case req.EntryId != 0 && findEntryForFloor(res, req.EntryId) != 0:
|
||||
// req.EntryId doubles as a floor id in some map flows
|
||||
}
|
||||
if resolvedEntry == 0 && req.EntryId != 0 {
|
||||
resolvedEntry = findEntryForFloor(res, req.EntryId)
|
||||
case req.TeleportId != 0:
|
||||
}
|
||||
if resolvedEntry == 0 && req.TeleportId != 0 {
|
||||
floorID := uint32(0)
|
||||
if sceneIdent != nil {
|
||||
floorID = sceneIdent.FloorId
|
||||
@@ -133,21 +158,26 @@ func init() {
|
||||
if entry := findEntryByTeleportInFloor(res, req.TeleportId, floorID); entry != 0 {
|
||||
resolvedEntry = entry
|
||||
} else {
|
||||
resolvedEntry = findEntryByTeleport(res, req.TeleportId, currentEntry())
|
||||
resolvedEntry = findEntryByTeleport(res, req.TeleportId, 0)
|
||||
}
|
||||
case sceneIdent != nil && sceneIdent.TeleportNigger != nil:
|
||||
}
|
||||
if resolvedEntry == 0 && sceneIdent != nil && sceneIdent.TeleportNigger != nil {
|
||||
if t, okc := sceneIdent.TeleportNigger.(*goproto.SceneIdentifier_MDAIDPPKOPO); okc && t.MDAIDPPKOPO != nil && t.MDAIDPPKOPO.PLIDNBMCIJH != 0 {
|
||||
// map teleport click: teleport id + floor arrive via scene_identifier
|
||||
tpID := t.MDAIDPPKOPO.PLIDNBMCIJH
|
||||
if entry := findEntryByTeleportInFloor(res, tpID, sceneIdent.FloorId); entry != 0 {
|
||||
resolvedEntry = entry
|
||||
} else {
|
||||
resolvedEntry = findEntryByTeleport(res, tpID, currentEntry())
|
||||
resolvedEntry = findEntryByTeleport(res, tpID, 0)
|
||||
}
|
||||
}
|
||||
case interactID != 0:
|
||||
resolvedEntry = findEntryByInteract(res, interactID, currentEntry())
|
||||
default:
|
||||
}
|
||||
if resolvedEntry == 0 && interactID != 0 {
|
||||
resolvedEntry = findEntryByInteract(res, interactID, 0)
|
||||
}
|
||||
if resolvedEntry == 0 && req.EntryId != 0 {
|
||||
resolvedEntry = req.EntryId
|
||||
}
|
||||
if resolvedEntry == 0 {
|
||||
resolvedEntry = currentEntry()
|
||||
}
|
||||
|
||||
@@ -215,6 +245,10 @@ func init() {
|
||||
// a wrong state (like 1100) breaks interactions and UI entry points
|
||||
state := uint32(1)
|
||||
if p, ok := s.PropEntities[req.PropEntityId]; ok {
|
||||
if p.State == 0 {
|
||||
p.State = 1
|
||||
s.PropEntities[req.PropEntityId] = p
|
||||
}
|
||||
state = p.State
|
||||
}
|
||||
|
||||
@@ -354,7 +388,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
player.SavePersistent()
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -396,16 +429,19 @@ func init() {
|
||||
// cmd 1432 -> 1421: unlock every teleport in the game
|
||||
handle("GetUnlockTeleport", func(s *Session, req *goproto.GetUnlockTeleportCsReq, rsp *goproto.GetUnlockTeleportScRsp) error {
|
||||
rsp.Retcode = 0
|
||||
unlocks := getMissionUnlocks()
|
||||
seen := map[uint32]bool{}
|
||||
var tps []uint32
|
||||
|
||||
// 1. All IDs from mission_unlocks.json
|
||||
unlocks := getMissionUnlocks()
|
||||
for _, id := range unlocks.TeleportIDs {
|
||||
if id != 0 && !seen[id] {
|
||||
seen[id] = true
|
||||
tps = append(tps, id)
|
||||
}
|
||||
}
|
||||
if len(tps) == 0 {
|
||||
|
||||
// 2. All IDs from teleports DB across all floors
|
||||
for _, m := range common.Teleports() {
|
||||
for id := range m {
|
||||
if id != 0 && !seen[id] {
|
||||
@@ -414,13 +450,85 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. All teleport IDs from LevelOutputConfigs
|
||||
if res, err := common.GameRes(); err == nil {
|
||||
for _, m := range res.LevelOutputConfigs {
|
||||
for _, cfg := range m {
|
||||
for _, sc := range cfg.Scenes {
|
||||
for tid := range sc.Teleports {
|
||||
if tid != 0 && !seen[tid] {
|
||||
seen[tid] = true
|
||||
tps = append(tps, tid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Any entry IDs requested by client or in EntranceIDs
|
||||
for _, eid := range req.EntryIdList {
|
||||
if eid != 0 && !seen[eid] {
|
||||
seen[eid] = true
|
||||
tps = append(tps, eid)
|
||||
}
|
||||
}
|
||||
for _, id := range unlocks.EntranceIDs {
|
||||
if id != 0 && !seen[id] {
|
||||
seen[id] = true
|
||||
tps = append(tps, id)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(tps, func(i, j int) bool { return tps[i] < tps[j] })
|
||||
rsp.UnlockedTeleportList = tps
|
||||
|
||||
SceneDebugLog("GET_UNLOCK_TELEPORT req_entries=%d teleports_sent=%d", len(req.EntryIdList), len(rsp.UnlockedTeleportList))
|
||||
return nil
|
||||
})
|
||||
|
||||
// cmd 1438 -> 1409: handle entity teleport within scene
|
||||
handle("SceneEntityTeleport", func(s *Session, req *goproto.SceneEntityTeleportCsReq, rsp *goproto.SceneEntityTeleportScRsp) error {
|
||||
rsp.Retcode = 0
|
||||
rsp.EntityMotion = req.EntityMotion
|
||||
if req.EntityMotion != nil && req.EntityMotion.Motion != nil {
|
||||
motion := req.EntityMotion.Motion
|
||||
player := s.JSON()
|
||||
if player != nil {
|
||||
if motion.Pos != nil {
|
||||
player.Position.X = motion.Pos.X
|
||||
player.Position.Y = motion.Pos.Y
|
||||
player.Position.Z = motion.Pos.Z
|
||||
}
|
||||
if motion.Rot != nil {
|
||||
player.Position.RotY = motion.Rot.Y
|
||||
}
|
||||
player.SavePersistent()
|
||||
}
|
||||
}
|
||||
SceneDebugLog("SCENE_ENTITY_TELEPORT entry_id=%d", req.EntryId)
|
||||
return nil
|
||||
})
|
||||
|
||||
// cmd 1267 -> 1263: teleport to mission reset point
|
||||
handle("TeleportToMissionResetPoint", func(s *Session, req *goproto.TeleportToMissionResetPointCsReq, rsp *goproto.TeleportToMissionResetPointScRsp) error {
|
||||
rsp.Retcode = 0
|
||||
player := s.JSON()
|
||||
if player != nil {
|
||||
rsp.Motion = &goproto.MotionInfo{
|
||||
Pos: &goproto.Vector{
|
||||
X: player.Position.X,
|
||||
Y: player.Position.Y,
|
||||
Z: player.Position.Z,
|
||||
},
|
||||
Rot: &goproto.Vector{
|
||||
Y: player.Position.RotY,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// findEntryForFloor resolves an entry id for a floor WITHOUT relying on
|
||||
|
||||
Binary file not shown.
+17
-12
@@ -1,24 +1,29 @@
|
||||
{
|
||||
"lineups": {
|
||||
"0": 1506,
|
||||
"1": 1501,
|
||||
"2": 1502,
|
||||
"3": 1503
|
||||
"0": 1204,
|
||||
"1": 1213,
|
||||
"2": 1308,
|
||||
"3": 1409
|
||||
},
|
||||
"position": {
|
||||
"x": -13238,
|
||||
"y": 592,
|
||||
"z": 86502,
|
||||
"rot_y": 290000
|
||||
"x": -92,
|
||||
"y": 4278,
|
||||
"z": 68083,
|
||||
"rot_y": 155566
|
||||
},
|
||||
"scene": {
|
||||
"plane_id": 20331,
|
||||
"floor_id": 20331001,
|
||||
"entry_id": 2033101
|
||||
"plane_id": 10202,
|
||||
"floor_id": 10202001,
|
||||
"entry_id": 102020107
|
||||
},
|
||||
"main_character": "FemaleHarmony",
|
||||
"march_type": "MarchHunt",
|
||||
"enable_sw_global": true,
|
||||
"enable_castorice_global": true,
|
||||
"dressed_skins": {}
|
||||
"dressed_skins": {},
|
||||
"ex_ranks_unlocked": {
|
||||
"1204": true,
|
||||
"1213": true,
|
||||
"1308": true
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,7 @@ var CmdIDByName = map[string]uint16{
|
||||
"StartQuickCocoonStageCsReq": 1305,
|
||||
"StartQuickCocoonStageRsp": 1371,
|
||||
"StartQuickFarmElementCsReq": 1359,
|
||||
"StartQuickFarmElementRsp": 1318,
|
||||
"CocoonSweepCsReq": 1306,
|
||||
"CocoonSweepScRsp": 1370,
|
||||
"FarmElementSweepCsReq": 1327,
|
||||
@@ -671,6 +672,7 @@ var CmdIDByName = map[string]uint16{
|
||||
"GetFiveDimMoneyDataCsReq": 2275,
|
||||
"GetFiveDimMoneyDataScRsp": 2260,
|
||||
"GetFriendListInfoCsReq": 2974,
|
||||
"GetFriendListInfoScRsp": 2919,
|
||||
"GetPlayerDetailInfoCsReq": 2931,
|
||||
"GetPlayerDetailInfoScRsp": 2987,
|
||||
"GetFriendApplyListInfoCsReq": 2905,
|
||||
@@ -2170,6 +2172,7 @@ var NameByCmdID = map[uint16]string{
|
||||
1305: "StartQuickCocoonStageCsReq",
|
||||
1371: "StartQuickCocoonStageRsp",
|
||||
1359: "StartQuickFarmElementCsReq",
|
||||
1318: "StartQuickFarmElementRsp",
|
||||
1306: "CocoonSweepCsReq",
|
||||
1370: "CocoonSweepScRsp",
|
||||
1327: "FarmElementSweepCsReq",
|
||||
@@ -2789,6 +2792,7 @@ var NameByCmdID = map[uint16]string{
|
||||
2275: "GetFiveDimMoneyDataCsReq",
|
||||
2260: "GetFiveDimMoneyDataScRsp",
|
||||
2974: "GetFriendListInfoCsReq",
|
||||
2919: "GetFriendListInfoScRsp",
|
||||
2931: "GetPlayerDetailInfoCsReq",
|
||||
2987: "GetPlayerDetailInfoScRsp",
|
||||
2905: "GetFriendApplyListInfoCsReq",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,764 @@
|
||||
{
|
||||
"player_icon_config": [
|
||||
{
|
||||
"ID": 201001,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1001.png",
|
||||
"AvatarID": 1001,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201002,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1002.png",
|
||||
"AvatarID": 1002,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201003,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1003.png",
|
||||
"AvatarID": 1003,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201004,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1004.png",
|
||||
"AvatarID": 1004,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201005,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1005.png",
|
||||
"AvatarID": 1005,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 90
|
||||
},
|
||||
{
|
||||
"ID": 201006,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1006.png",
|
||||
"AvatarID": 1006,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 90
|
||||
},
|
||||
{
|
||||
"ID": 201008,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1008.png",
|
||||
"AvatarID": 1008,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201009,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1009.png",
|
||||
"AvatarID": 1009,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201013,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1013.png",
|
||||
"AvatarID": 1013,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201101,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1101.png",
|
||||
"AvatarID": 1101,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201102,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1102.png",
|
||||
"AvatarID": 1102,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201103,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1103.png",
|
||||
"AvatarID": 1103,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201104,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1104.png",
|
||||
"AvatarID": 1104,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201105,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1105.png",
|
||||
"AvatarID": 1105,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201106,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1106.png",
|
||||
"AvatarID": 1106,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201107,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1107.png",
|
||||
"AvatarID": 1107,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201108,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1108.png",
|
||||
"AvatarID": 1108,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201109,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1109.png",
|
||||
"AvatarID": 1109,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 61
|
||||
},
|
||||
{
|
||||
"ID": 201110,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1110.png",
|
||||
"AvatarID": 1110,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 130
|
||||
},
|
||||
{
|
||||
"ID": 201111,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1111.png",
|
||||
"AvatarID": 1111,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 120
|
||||
},
|
||||
{
|
||||
"ID": 201112,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1112.png",
|
||||
"AvatarID": 1112,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 140
|
||||
},
|
||||
{
|
||||
"ID": 201201,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1201.png",
|
||||
"AvatarID": 1201,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 90
|
||||
},
|
||||
{
|
||||
"ID": 201202,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1202.png",
|
||||
"AvatarID": 1202,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 90
|
||||
},
|
||||
{
|
||||
"ID": 201203,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1203.png",
|
||||
"AvatarID": 1203,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 90
|
||||
},
|
||||
{
|
||||
"ID": 201204,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1204.png",
|
||||
"AvatarID": 1204,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 64
|
||||
},
|
||||
{
|
||||
"ID": 201205,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1205.png",
|
||||
"AvatarID": 1205,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 120
|
||||
},
|
||||
{
|
||||
"ID": 201206,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1206.png",
|
||||
"AvatarID": 1206,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 70
|
||||
},
|
||||
{
|
||||
"ID": 201207,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1207.png",
|
||||
"AvatarID": 1207,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 110
|
||||
},
|
||||
{
|
||||
"ID": 201208,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1208.png",
|
||||
"AvatarID": 1208,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 130
|
||||
},
|
||||
{
|
||||
"ID": 201209,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1209.png",
|
||||
"AvatarID": 1209,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 90
|
||||
},
|
||||
{
|
||||
"ID": 201210,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1210.png",
|
||||
"AvatarID": 1210,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 140
|
||||
},
|
||||
{
|
||||
"ID": 201211,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1211.png",
|
||||
"AvatarID": 1211,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 90
|
||||
},
|
||||
{
|
||||
"ID": 201212,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1212.png",
|
||||
"AvatarID": 1212,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 140
|
||||
},
|
||||
{
|
||||
"ID": 201213,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1213.png",
|
||||
"AvatarID": 1213,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 120
|
||||
},
|
||||
{
|
||||
"ID": 201214,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1214.png",
|
||||
"AvatarID": 1214,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 160
|
||||
},
|
||||
{
|
||||
"ID": 201215,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1215.png",
|
||||
"AvatarID": 1215,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 150
|
||||
},
|
||||
{
|
||||
"ID": 201217,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1217.png",
|
||||
"AvatarID": 1217,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 150
|
||||
},
|
||||
{
|
||||
"ID": 201218,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1218.png",
|
||||
"AvatarID": 1218,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 240
|
||||
},
|
||||
{
|
||||
"ID": 201220,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1220.png",
|
||||
"AvatarID": 1220,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 250
|
||||
},
|
||||
{
|
||||
"ID": 201221,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1221.png",
|
||||
"AvatarID": 1221,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 240
|
||||
},
|
||||
{
|
||||
"ID": 201222,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1222.png",
|
||||
"AvatarID": 1222,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 250
|
||||
},
|
||||
{
|
||||
"ID": 201223,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1223.png",
|
||||
"AvatarID": 1223,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 250
|
||||
},
|
||||
{
|
||||
"ID": 201224,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1224.png",
|
||||
"AvatarID": 1224,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 240
|
||||
},
|
||||
{
|
||||
"ID": 201225,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1225.png",
|
||||
"AvatarID": 1225,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 270
|
||||
},
|
||||
{
|
||||
"ID": 201301,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1301.png",
|
||||
"AvatarID": 1301,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 210
|
||||
},
|
||||
{
|
||||
"ID": 201302,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1302.png",
|
||||
"AvatarID": 1302,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 150
|
||||
},
|
||||
{
|
||||
"ID": 201303,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1303.png",
|
||||
"AvatarID": 1303,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 160
|
||||
},
|
||||
{
|
||||
"ID": 201304,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1304.png",
|
||||
"AvatarID": 1304,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 210
|
||||
},
|
||||
{
|
||||
"ID": 201305,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1305.png",
|
||||
"AvatarID": 1305,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 160
|
||||
},
|
||||
{
|
||||
"ID": 201306,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1306.png",
|
||||
"AvatarID": 1306,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 200
|
||||
},
|
||||
{
|
||||
"ID": 201307,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1307.png",
|
||||
"AvatarID": 1307,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 200
|
||||
},
|
||||
{
|
||||
"ID": 201308,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1308.png",
|
||||
"AvatarID": 1308,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 210
|
||||
},
|
||||
{
|
||||
"ID": 201309,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1309.png",
|
||||
"AvatarID": 1309,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 220
|
||||
},
|
||||
{
|
||||
"ID": 201310,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1310.png",
|
||||
"AvatarID": 1310,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 230
|
||||
},
|
||||
{
|
||||
"ID": 201312,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1312.png",
|
||||
"AvatarID": 1312,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 200
|
||||
},
|
||||
{
|
||||
"ID": 201313,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1313.png",
|
||||
"AvatarID": 1313,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 270
|
||||
},
|
||||
{
|
||||
"ID": 201314,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1314.png",
|
||||
"AvatarID": 1314,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 230
|
||||
},
|
||||
{
|
||||
"ID": 201315,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1315.png",
|
||||
"AvatarID": 1315,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 220
|
||||
},
|
||||
{
|
||||
"ID": 201317,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1317.png",
|
||||
"AvatarID": 1317,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 260
|
||||
},
|
||||
{
|
||||
"ID": 201321,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1321.png",
|
||||
"AvatarID": 1321,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 380
|
||||
},
|
||||
{
|
||||
"ID": 201401,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1401.png",
|
||||
"AvatarID": 1401,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 300
|
||||
},
|
||||
{
|
||||
"ID": 201402,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1402.png",
|
||||
"AvatarID": 1402,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 300
|
||||
},
|
||||
{
|
||||
"ID": 201403,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1403.png",
|
||||
"AvatarID": 1403,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 310
|
||||
},
|
||||
{
|
||||
"ID": 201404,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1404.png",
|
||||
"AvatarID": 1404,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 310
|
||||
},
|
||||
{
|
||||
"ID": 201405,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1405.png",
|
||||
"AvatarID": 1405,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 320
|
||||
},
|
||||
{
|
||||
"ID": 201406,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1406.png",
|
||||
"AvatarID": 1406,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 330
|
||||
},
|
||||
{
|
||||
"ID": 201407,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1407.png",
|
||||
"AvatarID": 1407,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 320
|
||||
},
|
||||
{
|
||||
"ID": 201408,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1408.png",
|
||||
"AvatarID": 1408,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 340
|
||||
},
|
||||
{
|
||||
"ID": 201409,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1409.png",
|
||||
"AvatarID": 1409,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 330
|
||||
},
|
||||
{
|
||||
"ID": 201410,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1410.png",
|
||||
"AvatarID": 1410,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 350
|
||||
},
|
||||
{
|
||||
"ID": 201412,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1412.png",
|
||||
"AvatarID": 1412,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 350
|
||||
},
|
||||
{
|
||||
"ID": 201413,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1413.png",
|
||||
"AvatarID": 1413,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 360
|
||||
},
|
||||
{
|
||||
"ID": 201414,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1414.png",
|
||||
"AvatarID": 1414,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 360
|
||||
},
|
||||
{
|
||||
"ID": 201415,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1415.png",
|
||||
"AvatarID": 1415,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 370
|
||||
},
|
||||
{
|
||||
"ID": 201501,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1501.png",
|
||||
"AvatarID": 1501,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 400
|
||||
},
|
||||
{
|
||||
"ID": 201502,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1502.png",
|
||||
"AvatarID": 1502,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 400
|
||||
},
|
||||
{
|
||||
"ID": 201504,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1504.png",
|
||||
"AvatarID": 1504,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 410
|
||||
},
|
||||
{
|
||||
"ID": 201505,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1505.png",
|
||||
"AvatarID": 1505,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 420
|
||||
},
|
||||
{
|
||||
"ID": 201506,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1506.png",
|
||||
"AvatarID": 1506,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 420
|
||||
},
|
||||
{
|
||||
"ID": 208001,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8001.png",
|
||||
"AvatarID": 8001,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 63
|
||||
},
|
||||
{
|
||||
"ID": 208002,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8002.png",
|
||||
"AvatarID": 8002,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 63
|
||||
},
|
||||
{
|
||||
"ID": 208003,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8003.png",
|
||||
"AvatarID": 8003,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 63
|
||||
},
|
||||
{
|
||||
"ID": 208004,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8004.png",
|
||||
"AvatarID": 8004,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 63
|
||||
},
|
||||
{
|
||||
"ID": 208005,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8005.png",
|
||||
"AvatarID": 8005,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 220
|
||||
},
|
||||
{
|
||||
"ID": 208006,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8006.png",
|
||||
"AvatarID": 8006,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 220
|
||||
},
|
||||
{
|
||||
"ID": 208007,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8007.png",
|
||||
"AvatarID": 8007,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 370
|
||||
},
|
||||
{
|
||||
"ID": 208008,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8008.png",
|
||||
"AvatarID": 8008,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 370
|
||||
},
|
||||
{
|
||||
"ID": 208009,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8009.png",
|
||||
"AvatarID": 8009,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 420
|
||||
},
|
||||
{
|
||||
"ID": 208010,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/8010.png",
|
||||
"AvatarID": 8010,
|
||||
"Type": "Hero",
|
||||
"SortType": 3,
|
||||
"Sort": 420
|
||||
},
|
||||
{
|
||||
"ID": 201507,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1507.png",
|
||||
"AvatarID": 1507,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 430
|
||||
},
|
||||
{
|
||||
"ID": 201510,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1510.png",
|
||||
"AvatarID": 1510,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 440
|
||||
},
|
||||
{
|
||||
"ID": 201503,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1503.png",
|
||||
"AvatarID": 1503,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 460
|
||||
},
|
||||
{
|
||||
"ID": 201512,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1512.png",
|
||||
"AvatarID": 1512,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 450
|
||||
},
|
||||
{
|
||||
"ID": 201513,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1513.png",
|
||||
"AvatarID": 1513,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 450
|
||||
},
|
||||
{
|
||||
"ID": 201511,
|
||||
"ImagePath": "SpriteOutput/AvatarRoundIcon/Avatar/1511.png",
|
||||
"AvatarID": 1511,
|
||||
"Type": "Avatar",
|
||||
"SortType": 3,
|
||||
"Sort": 470
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,835 @@
|
||||
{
|
||||
"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 dealt by all allies ignores 30% of enemy targets' All-Type RES."
|
||||
}
|
||||
],
|
||||
"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": "When an ally target consumes 1 Skill Point, increases the DMG dealt by all allies by 6%, stacking up to #2[i] time(s)."
|
||||
},
|
||||
{
|
||||
"id": 3111063,
|
||||
"name": "After defeating enemy targets, increases Break DMG taken by all enemies by 15% to a max of #2[i] stack(s). This effect will also take effect on new enemies entering the battlefield."
|
||||
},
|
||||
{
|
||||
"id": 3111061,
|
||||
"name": "After any ally target consumes their own HP, their CRIT DMG increases by 55%, lasting for #2[i] turn(s)."
|
||||
}
|
||||
],
|
||||
"BuffList2": [
|
||||
{
|
||||
"id": 3111059,
|
||||
"name": "Increases Ultimate DMG dealt by all allies by 30%, and additionally increases by 30% against enemy targets in Weakness Broken state."
|
||||
},
|
||||
{
|
||||
"id": 3111060,
|
||||
"name": "When allies use Basic ATK or Skill to attack Weakness Broken enemies, all DoTs currently applied to the enemies will immediately deal DMG equal to 15% of the original DMG."
|
||||
},
|
||||
{
|
||||
"id": 3111064,
|
||||
"name": "After the character in position 1 in the team uses Ultimate, their Weakness Break Efficiency increases by 50% and increases CRIT DMG by 50%, lasting for #3[i] turn(s)."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"GroupID": 3012,
|
||||
"BuffList1": [
|
||||
{
|
||||
"id": 3111068,
|
||||
"name": "Increases Physical DMG dealt by all allies by 50%, and decreases all enemies' Physical RES by 20%."
|
||||
},
|
||||
{
|
||||
"id": 3111069,
|
||||
"name": "When allies use Basic ATK or Skill to attack Weakness Broken enemies, all DoTs currently applied to the enemies will immediately produce DMG equal to 20% of the original DMG."
|
||||
},
|
||||
{
|
||||
"id": 3111070,
|
||||
"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)."
|
||||
}
|
||||
],
|
||||
"BuffList2": [
|
||||
{
|
||||
"id": 3111065,
|
||||
"name": "DMG dealt by memosprites ignores 20% of the target's DEF."
|
||||
},
|
||||
{
|
||||
"id": 3111066,
|
||||
"name": "Increases all allies' Skill DMG by 30%. At the start of their turn, if their current HP percentage is equal to or lower than 1%, restores HP equal to 15% of their Max HP."
|
||||
},
|
||||
{
|
||||
"id": 3111067,
|
||||
"name": "When a character uses Ultimate, their Weakness Break Efficiency increases by 100% and CRIT DMG increases by 40%, lasting for #3[i] turn(s)."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"GroupID": 3013,
|
||||
"BuffList1": [
|
||||
{
|
||||
"id": 3111068,
|
||||
"name": "Increases Physical DMG dealt by all allies by 50%, and decreases all enemies' Physical RES by 20%."
|
||||
},
|
||||
{
|
||||
"id": 3111069,
|
||||
"name": "When allies use Basic ATK or Skill to attack Weakness Broken enemies, all DoTs currently applied to the enemies will immediately produce DMG equal to 20% of the original DMG."
|
||||
},
|
||||
{
|
||||
"id": 3111070,
|
||||
"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)."
|
||||
}
|
||||
],
|
||||
"BuffList2": [
|
||||
{
|
||||
"id": 3111065,
|
||||
"name": "DMG dealt by memosprites ignores 20% of the target's DEF."
|
||||
},
|
||||
{
|
||||
"id": 3111066,
|
||||
"name": "Increases all allies' Skill DMG by 30%. At the start of their turn, if their current HP percentage is equal to or lower than 1%, restores HP equal to 15% of their Max HP."
|
||||
},
|
||||
{
|
||||
"id": 3111067,
|
||||
"name": "When a character uses Ultimate, their Weakness Break Efficiency increases by 100% and CRIT DMG increases by 40%, lasting for #3[i] turn(s)."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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 the 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 combat, and after they take action, additionally adds #2[i] stack(s) of the 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 the 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": "During \"Surging Grit,\" memosprite's DMG increases by 30%, and when memosprite disappears, memomaster's action advances by 15%."
|
||||
},
|
||||
{
|
||||
"id": 3031345,
|
||||
"name": "Upon entering \"Surging Grit,\" immediately recover #1[i] Skill Point(s). During \"Surging Grit,\" enemy targets take 30% more DoT."
|
||||
},
|
||||
{
|
||||
"id": 3031346,
|
||||
"name": "During \"Surging Grit,\" after an ally uses a Skill to attack enemy targets, they deal additional True DMG equal to 30% of the original damage to each enemy target hit."
|
||||
}
|
||||
],
|
||||
"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": 2018,
|
||||
"BuffList1": [
|
||||
{
|
||||
"id": 3031347,
|
||||
"name": "During \"Surging Grit\", when an ally target uses their Ultimate, increases all allies DMG dealt by 12%, stacking up to #2[i] times. This effect lasts until \"Surging Grit\" ends."
|
||||
},
|
||||
{
|
||||
"id": 3031348,
|
||||
"name": "Upon entering \"Surging Grit,\" immediately recover #1[i] Skill Point(s). During \"Surging Grit,\" enemy targets take 30% more DoT."
|
||||
},
|
||||
{
|
||||
"id": 3031349,
|
||||
"name": "During \"Surging Grit\", increases all allies SPD by 20%, and after using a Skill, apply #2[i] stack(s) of \"Resound\" to all allies."
|
||||
}
|
||||
],
|
||||
"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."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"challenge_peak_boss_config": [
|
||||
{
|
||||
"ID": 104,
|
||||
"HardTitle": {
|
||||
"Hash": -829434279,
|
||||
"Hash64": 7760170859122248016
|
||||
},
|
||||
"BuffList": [3033006, 3033007, 3033008],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30501022],
|
||||
"HardTagList": [3033010, 3033013, 3033019]
|
||||
},
|
||||
{
|
||||
"ID": 204,
|
||||
"HardTitle": {
|
||||
"Hash": -829434376,
|
||||
"Hash64": 1050453030874697501
|
||||
},
|
||||
"BuffList": [3033020, 3033021, 3033022],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30502022],
|
||||
"HardTagList": [3033013, 3033019]
|
||||
},
|
||||
{
|
||||
"ID": 304,
|
||||
"HardTitle": {
|
||||
"Hash": -829434345,
|
||||
"Hash64": 18174992387478714442
|
||||
},
|
||||
"BuffList": [3033032, 3033033, 3033034],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30503022],
|
||||
"HardTagList": [3033029, 3033040, 3033036]
|
||||
},
|
||||
{
|
||||
"ID": 404,
|
||||
"HardTitle": {
|
||||
"Hash": -829434186,
|
||||
"Hash64": 11316176485030290708
|
||||
},
|
||||
"BuffList": [3033043, 3033044, 3033045],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30504022],
|
||||
"HardTagList": [3033026, 3033046]
|
||||
},
|
||||
{
|
||||
"ID": 504,
|
||||
"HardTitle": {
|
||||
"Hash": -829434155,
|
||||
"Hash64": 7086567855550187989
|
||||
},
|
||||
"BuffList": [3033048, 3033049, 3033050],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30505022],
|
||||
"HardTagList": [3033018, 3033026, 3033046]
|
||||
},
|
||||
{
|
||||
"ID": 604,
|
||||
"HardTitle": {
|
||||
"Hash": -829434252,
|
||||
"Hash64": 6674206141463462266
|
||||
},
|
||||
"BuffList": [3033053, 3033054, 3033055],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30506022],
|
||||
"HardTagList": [3033057, 3033042]
|
||||
},
|
||||
{
|
||||
"ID": 704,
|
||||
"HardTitle": {
|
||||
"Hash": -829434221,
|
||||
"Hash64": 4139559719394918406
|
||||
},
|
||||
"BuffList": [3033060, 3033061, 3033062],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30507022],
|
||||
"HardTagList": [3033059, 3033064]
|
||||
},
|
||||
{
|
||||
"ID": 804,
|
||||
"HardTitle": {
|
||||
"Hash": -829434046,
|
||||
"Hash64": 92562529814961898
|
||||
},
|
||||
"BuffList": [3033066, 3033068, 3033067],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30508022],
|
||||
"HardTagList": [3033070, 3033052]
|
||||
},
|
||||
{
|
||||
"ID": 904,
|
||||
"HardTitle": {
|
||||
"Hash": -829434015,
|
||||
"Hash64": 2996493158462877067
|
||||
},
|
||||
"BuffList": [3033073, 3033074, 3033087],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30509022],
|
||||
"HardTagList": [3033059, 3033072]
|
||||
},
|
||||
{
|
||||
"ID": 1004,
|
||||
"HardTitle": {
|
||||
"Hash": 968936817,
|
||||
"Hash64": 15826532082466368205
|
||||
},
|
||||
"BuffList": [3033082, 3033083, 3033084],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30510022],
|
||||
"HardTagList": [3033059, 3033079, 3033080]
|
||||
},
|
||||
{
|
||||
"ID": 1104,
|
||||
"HardTitle": {
|
||||
"Hash": -617350100,
|
||||
"Hash64": 13103456677221618482
|
||||
},
|
||||
"BuffList": [3033095, 3033074, 3033008],
|
||||
"ColorMedalTarget": 6,
|
||||
"HardTarget": 3007,
|
||||
"HardEventIDList": [30511022],
|
||||
"HardTagList": [3033057, 3033094, 3033026]
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"challenge_peak_group_config": [
|
||||
{
|
||||
"ID": 1,
|
||||
"Title": {
|
||||
"Hash": -504960136,
|
||||
"Hash64": 3550232802813737810
|
||||
},
|
||||
"RecommendID": 1,
|
||||
"ActivityModule": 2100101,
|
||||
"PreLevelIDList": [101, 102, 103],
|
||||
"BossLevelID": 104,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W4_Theoroi.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W4_Theoroi_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4001.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4001.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0001/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0001/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0001/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0001/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 2,
|
||||
"Title": {
|
||||
"Hash": -101675609,
|
||||
"Hash64": 10349426151264090008
|
||||
},
|
||||
"RecommendID": 2,
|
||||
"ActivityModule": 2100201,
|
||||
"PreLevelIDList": [201, 202, 203],
|
||||
"BossLevelID": 204,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W4_DawnsEye.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W4_DawnsEye_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4002.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4002.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4002.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0002/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0002/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0002/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0002/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 3,
|
||||
"Title": {
|
||||
"Hash": -1667759550,
|
||||
"Hash64": 7638058374000144758
|
||||
},
|
||||
"RecommendID": 3,
|
||||
"ActivityModule": 2100301,
|
||||
"PreLevelIDList": [301, 302, 303],
|
||||
"BossLevelID": 304,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W3_Sunday.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W3_Sunday_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4003.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4003.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4003.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0003/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0003/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0003/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0003/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 4,
|
||||
"Title": {
|
||||
"Hash": -1264475023,
|
||||
"Hash64": 5317369650647745266
|
||||
},
|
||||
"RecommendID": 4,
|
||||
"ActivityModule": 2100401,
|
||||
"PreLevelIDList": [401, 402, 403],
|
||||
"BossLevelID": 404,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W5_Vtuber.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W5_Vtuber_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4004.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4004.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4004.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0004/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0004/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0004/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0004/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 5,
|
||||
"Title": {
|
||||
"Hash": 1464408332,
|
||||
"Hash64": 9493455258062325921
|
||||
},
|
||||
"RecommendID": 5,
|
||||
"ActivityModule": 2100501,
|
||||
"PreLevelIDList": [501, 502, 503],
|
||||
"BossLevelID": 504,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W5_Pam.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W5_Pam_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4005.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4005.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4005.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0005/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0005/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0005/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0005/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 6,
|
||||
"Title": {
|
||||
"Hash": 1867692859,
|
||||
"Hash64": 5565801180195930457
|
||||
},
|
||||
"RecommendID": 6,
|
||||
"ActivityModule": 2100601,
|
||||
"PreLevelIDList": [601, 602, 603],
|
||||
"BossLevelID": 604,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W5_Ripper.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W5_Ripper_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4006.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4006.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4006.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0006/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0006/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0006/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0006/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 7,
|
||||
"Title": {
|
||||
"Hash": 301608918,
|
||||
"Hash64": 7011664678968873757
|
||||
},
|
||||
"RecommendID": 7,
|
||||
"ActivityModule": 2100701,
|
||||
"PreLevelIDList": [701, 702, 703],
|
||||
"BossLevelID": 704,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_SW_Boss_01.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_SW_01_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4007.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4007.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4007.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0007/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0007/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0007/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0007/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 8,
|
||||
"Title": {
|
||||
"Hash": 704893445,
|
||||
"Hash64": 13457040013447238093
|
||||
},
|
||||
"RecommendID": 8,
|
||||
"ActivityModule": 2100801,
|
||||
"PreLevelIDList": [801, 802, 803],
|
||||
"BossLevelID": 804,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W5_Painter.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W5_Painter_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4008.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4008.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4008.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0008/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0008/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0008/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0008/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 9,
|
||||
"Title": {
|
||||
"Hash": -861190496,
|
||||
"Hash64": 556488733392366321
|
||||
},
|
||||
"RecommendID": 9,
|
||||
"ActivityModule": 2100901,
|
||||
"PreLevelIDList": [901, 902, 903],
|
||||
"BossLevelID": 904,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W4_IronTombCore.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W4_IronTombCore_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4009.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4009.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4009.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0009/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0009/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0009/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0009/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 10,
|
||||
"Title": {
|
||||
"Hash": 1570201704,
|
||||
"Hash64": 4908466715646711625
|
||||
},
|
||||
"RecommendID": 10,
|
||||
"ActivityModule": 2101001,
|
||||
"PreLevelIDList": [1001, 1002, 1003],
|
||||
"BossLevelID": 1004,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W5_Fallen.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W5_Fallen_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4010.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4010.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4010.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0010/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0010/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0010/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0010/Ultra.png"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": 11,
|
||||
"Title": {
|
||||
"Hash": 1570201705,
|
||||
"Hash64": 8621814597265561652
|
||||
},
|
||||
"RecommendID": 11,
|
||||
"ActivityModule": 2101101,
|
||||
"PreLevelIDList": [1101, 1102, 1103],
|
||||
"BossLevelID": 1104,
|
||||
"RewardGroupID": 1,
|
||||
"BossUI3DPrefabPath": "UI/UI3D/ChallengePeak/_dependencies/Prefab/ChallengePeakBoss_W5_AsatPramad.prefab",
|
||||
"BossUI3DAnimatorPath": "UI/UI3D/ChallengePeak/_dependencies/Animator/ChallengePeakBoss_W5_AsatPramad_AniControl.controller",
|
||||
"ThemePosterTabPicPath": "SpriteOutput/Quest/TabIcon/BtnChallengePeak_4011.png",
|
||||
"ThemeIconPicPath": "SpriteOutput/ChallengePeak/ChallengePeakIcon_4011.png",
|
||||
"HandBookPanelBannerPath": "SpriteOutput/DailyMission/Banner/ChallengePeakPanelBanner_4011.png",
|
||||
"RankIconPathList": [
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0011/Bronze.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0011/Silver.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0011/Gold.png",
|
||||
"SpriteOutput/ChallengePeak/MedallionIcon/ChallengePeakMedalIcon0011/Ultra.png"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+344390
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,456 @@
|
||||
{
|
||||
"stage_invasion_config": [
|
||||
{
|
||||
"StageID": 30509012,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5013010,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5014010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 420503,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 202206017,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 202303203,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 420504,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 202206018,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 202303204,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30324032,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 2032020,
|
||||
"LMEBOHHDIAG": [1, 0, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 2002030,
|
||||
"LMEBOHHDIAG": [1, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 8012020,
|
||||
"LMEBOHHDIAG": [1, 0, 0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30324042,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5012100,
|
||||
"LMEBOHHDIAG": [1, 0, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 4062020,
|
||||
"LMEBOHHDIAG": [1, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 2012010,
|
||||
"LMEBOHHDIAG": [1, 0, 1, 0, 0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30510011,
|
||||
"InvasionID": 3,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5014020,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5013040,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 420533,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 800302201,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 300301401,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 420534,
|
||||
"InvasionID": 3,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 800302201,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 300301401,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30325032,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 300305007,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 1002030,
|
||||
"LMEBOHHDIAG": [1, 0, 1, 0, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 2002030,
|
||||
"LMEBOHHDIAG": [1, 0, 1, 0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30325042,
|
||||
"InvasionID": 3,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 300302007,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5012120,
|
||||
"LMEBOHHDIAG": [1, 0, 1, 0, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 2032010,
|
||||
"LMEBOHHDIAG": [1, 0, 1, 0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30125112,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 4063010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30125121,
|
||||
"InvasionID": 3,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5014010,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 8003010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30126112,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 802401106,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30126123,
|
||||
"InvasionID": 3,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 4064010,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 4062010,
|
||||
"LMEBOHHDIAG": [1, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 4063010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30127112,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 1003010,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 4034010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30127123,
|
||||
"InvasionID": 3,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5014020,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5012120,
|
||||
"LMEBOHHDIAG": [1, 0, 0, 1]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30511012,
|
||||
"InvasionID": 4,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5014030,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5023020,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 502302001,
|
||||
"LMEBOHHDIAG": [0, 0, 1]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30511021,
|
||||
"InvasionID": 4,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 801503006,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 801204002,
|
||||
"LMEBOHHDIAG": [0, 1, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 801505001,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30511022,
|
||||
"InvasionID": 4,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 801503006,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 801204002,
|
||||
"LMEBOHHDIAG": [0, 1, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 801505001,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 420563,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5013052,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5013042,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5013072,
|
||||
"LMEBOHHDIAG": [0, 1]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 420564,
|
||||
"InvasionID": 3,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5013052,
|
||||
"LMEBOHHDIAG": [1, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5013042,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5013072,
|
||||
"LMEBOHHDIAG": [0, 1]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30326032,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 8032020,
|
||||
"LMEBOHHDIAG": [1, 0, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 2032010,
|
||||
"LMEBOHHDIAG": [1, 0, 0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 30326043,
|
||||
"InvasionID": 3,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 8012040,
|
||||
"LMEBOHHDIAG": [1, 0]
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5012020,
|
||||
"LMEBOHHDIAG": [1, 0]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 205710700,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5032040,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5033010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 205710701,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5032040,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5033010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 205710702,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5032040,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5033010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 205710703,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5032040,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5033010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 205710704,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5032040,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5033010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 205710705,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5032040,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5033010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StageID": 205710706,
|
||||
"InvasionID": 2,
|
||||
"MonsterInvasionList": [
|
||||
{
|
||||
"DBLDCKODNEN": 5032040,
|
||||
"LMEBOHHDIAG": []
|
||||
},
|
||||
{
|
||||
"DBLDCKODNEN": 5033010,
|
||||
"LMEBOHHDIAG": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1223028
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,164 @@
|
||||
@echo off
|
||||
title GaC hkrpg GO Server (4.6.53)
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo ==============================================================================
|
||||
echo [ GaC hkrpg GO ] StarRail 4.6.53 Server and Launcher
|
||||
echo gameserver : UDP 23301 (KCP Gateway)
|
||||
echo sdkserver : HTTP 21000 (Dispatch / Login / SRTools)
|
||||
echo ==============================================================================
|
||||
echo.
|
||||
echo [1] Start Server and Star Rail Game / Proxy (Default)
|
||||
echo [2] Start Both Servers (gameserver + sdkserver)
|
||||
echo [3] Start SDK Server ONLY (HTTP 21000)
|
||||
echo [4] Start Game Server ONLY (UDP 23301)
|
||||
echo [5] Start Server and Gay Proxy
|
||||
echo [6] Build / Rebuild binaries (Go)
|
||||
echo [7] Exit
|
||||
echo.
|
||||
echo ==============================================================================
|
||||
|
||||
set "choice=%~1"
|
||||
if not defined choice set /p choice="Select [1-7] (Press Enter for 1): "
|
||||
if not defined choice set "choice=1"
|
||||
set "choice=%choice: =%"
|
||||
if not defined choice set "choice=1"
|
||||
|
||||
if "%choice%"=="1" goto opt_game
|
||||
if "%choice%"=="2" goto opt_servers_only
|
||||
if "%choice%"=="3" goto opt_sdk_only
|
||||
if "%choice%"=="4" goto opt_gs_only
|
||||
if "%choice%"=="5" goto opt_gay_proxy
|
||||
if "%choice%"=="6" goto opt_build
|
||||
if "%choice%"=="7" exit /b 0
|
||||
|
||||
if /i "%choice%"=="sdk" goto opt_sdk_only
|
||||
if /i "%choice%"=="gs" goto opt_gs_only
|
||||
if /i "%choice%"=="servers" goto opt_servers_only
|
||||
if /i "%choice%"=="game" goto opt_game
|
||||
if /i "%choice%"=="proxy" goto opt_gay_proxy
|
||||
if /i "%choice%"=="build" goto opt_build
|
||||
|
||||
echo Unknown option "%choice%", starting servers only...
|
||||
goto opt_servers_only
|
||||
|
||||
:start_core_servers
|
||||
if not exist "gameserver-go.exe" (
|
||||
echo [ERROR] gameserver-go.exe not found! Please build first - Option 6
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
if not exist "sdkserver-go.exe" (
|
||||
echo [ERROR] sdkserver-go.exe not found! Please build first - Option 6
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [*] Cleaning old server processes...
|
||||
taskkill /f /im sdkserver-go.exe >nul 2>&1
|
||||
taskkill /f /im gameserver-go.exe >nul 2>&1
|
||||
|
||||
echo [*] Starting SDK Server [Port 21000]...
|
||||
start "GaC GO - SDK Server [Port 21000]" /d "%~dp0" cmd /k sdkserver-go.exe
|
||||
|
||||
echo [*] Starting Game Server [Port 23301]...
|
||||
start "GaC GO - Game Server [Port 23301]" /d "%~dp0" cmd /k gameserver-go.exe
|
||||
exit /b 0
|
||||
|
||||
:opt_sdk_only
|
||||
if not exist "sdkserver-go.exe" (
|
||||
echo [ERROR] sdkserver-go.exe not found! Please build first - Option 6
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo [*] Cleaning old sdkserver process...
|
||||
taskkill /f /im sdkserver-go.exe >nul 2>&1
|
||||
|
||||
echo [*] Starting SDK Server [Port 21000]...
|
||||
start "GaC GO - SDK Server [Port 21000]" /d "%~dp0" cmd /k sdkserver-go.exe
|
||||
goto finish
|
||||
|
||||
:opt_gs_only
|
||||
if not exist "gameserver-go.exe" (
|
||||
echo [ERROR] gameserver-go.exe not found! Please build first - Option 6
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo [*] Cleaning old gameserver process...
|
||||
taskkill /f /im gameserver-go.exe >nul 2>&1
|
||||
|
||||
echo [*] Starting Game Server [Port 23301]...
|
||||
start "GaC GO - Game Server [Port 23301]" /d "%~dp0" cmd /k gameserver-go.exe
|
||||
goto finish
|
||||
|
||||
:opt_game
|
||||
call :start_core_servers
|
||||
if errorlevel 1 goto :eof
|
||||
|
||||
echo.
|
||||
echo [*] Launching Star Rail Client / Proxy...
|
||||
set "GAME_DIR="
|
||||
if exist "%USERPROFILE%\Desktop\StarRail_4.6.53_OS\StarRail.exe" set "GAME_DIR=%USERPROFILE%\Desktop\StarRail_4.6.53_OS"
|
||||
if not defined GAME_DIR if exist "%~dp0..\StarRail_4.6.53_OS\StarRail.exe" set "GAME_DIR=%~dp0..\StarRail_4.6.53_OS"
|
||||
if not defined GAME_DIR if exist "%USERPROFILE%\Desktop\StarRail_4.6.51_OS\StarRail.exe" set "GAME_DIR=%USERPROFILE%\Desktop\StarRail_4.6.51_OS"
|
||||
if not defined GAME_DIR if exist "D:\HoYoPlay\games\Star Rail Games\StarRail.exe" set "GAME_DIR=D:\HoYoPlay\games\Star Rail Games"
|
||||
if not defined GAME_DIR if exist "%~dp0..\StarRail_4.6.51_OS\StarRail.exe" set "GAME_DIR=%~dp0..\StarRail_4.6.51_OS"
|
||||
|
||||
if defined GAME_DIR (
|
||||
echo [*] Found game client at: %GAME_DIR%
|
||||
if exist "%GAME_DIR%\GaCy Launcher.exe" (
|
||||
echo [*] Starting GaCy Launcher...
|
||||
start "GaCy Launcher" /d "%GAME_DIR%" "%GAME_DIR%\GaCy Launcher.exe"
|
||||
) else if exist "%GAME_DIR%\launcher.exe" (
|
||||
echo [*] Starting Launcher...
|
||||
start "Proxy Launcher" /d "%GAME_DIR%" "%GAME_DIR%\launcher.exe"
|
||||
) else if exist "%GAME_DIR%\StarRail.exe" (
|
||||
echo [*] Starting StarRail.exe...
|
||||
start "Star Rail Client" /d "%GAME_DIR%" "%GAME_DIR%\StarRail.exe"
|
||||
)
|
||||
) else (
|
||||
echo [!] Game client not found automatically. Please open StarRail manually.
|
||||
)
|
||||
goto finish
|
||||
|
||||
:opt_gay_proxy
|
||||
call :start_core_servers
|
||||
if errorlevel 1 goto :eof
|
||||
|
||||
echo.
|
||||
echo [*] Launching Gay Proxy...
|
||||
set "PROXY_BAT="
|
||||
if exist "%USERPROFILE%\Desktop\Gay Proxy\run_proxy.bat" set "PROXY_BAT=%USERPROFILE%\Desktop\Gay Proxy\run_proxy.bat"
|
||||
if not defined PROXY_BAT if exist "%~dp0..\Gay Proxy\run_proxy.bat" set "PROXY_BAT=%~dp0..\Gay Proxy\run_proxy.bat"
|
||||
|
||||
if defined PROXY_BAT (
|
||||
start "Gay Proxy" cmd /k "%PROXY_BAT%"
|
||||
) else (
|
||||
echo [!] Gay Proxy not found.
|
||||
)
|
||||
goto finish
|
||||
|
||||
:opt_servers_only
|
||||
call :start_core_servers
|
||||
if errorlevel 1 goto :eof
|
||||
goto finish
|
||||
|
||||
:opt_build
|
||||
where go >nul 2>nul || set "PATH=%PATH%;C:\Program Files\Go\bin"
|
||||
echo [*] Building gameserver-go.exe...
|
||||
go build -o gameserver-go.exe ./cmd/gameserver || (pause & goto :eof)
|
||||
echo [*] Building sdkserver-go.exe...
|
||||
go build -o sdkserver-go.exe ./cmd/sdkserver || (pause & goto :eof)
|
||||
echo [OK] Build successful!
|
||||
pause
|
||||
goto :eof
|
||||
|
||||
:finish
|
||||
echo.
|
||||
echo ==============================================================================
|
||||
echo [OK] Started successfully!
|
||||
echo Close server windows to stop them.
|
||||
echo Closing this launcher window in 5 seconds...
|
||||
echo ==============================================================================
|
||||
timeout /t 5 >nul 2>&1 || ping 127.0.0.1 -n 6 >nul
|
||||
exit
|
||||
+1
-46
@@ -1,48 +1,3 @@
|
||||
@echo off
|
||||
title GaC hkrpg GO Server (4.6.51)
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo ============================================================
|
||||
echo GaC hkrpg GO - StarRail 4.6.51 Private Server (Go)
|
||||
echo gameserver : UDP 23301 (KCP)
|
||||
echo sdkserver : HTTP 21000 (dispatch + login + SRTools)
|
||||
echo ============================================================
|
||||
echo.
|
||||
echo [1] Start server (gameserver + sdkserver)
|
||||
echo [2] Start gameserver only
|
||||
echo [3] Start sdkserver only
|
||||
echo [4] Build / rebuild exes (requires Go)
|
||||
echo [5] Exit
|
||||
echo.
|
||||
set /p choice="Select: "
|
||||
|
||||
if "%choice%"=="1" goto all
|
||||
if "%choice%"=="2" goto gs
|
||||
if "%choice%"=="3" goto sdk
|
||||
if "%choice%"=="4" goto build
|
||||
goto :eof
|
||||
|
||||
:all
|
||||
start "GaC GO sdkserver" cmd /k sdkserver-go.exe
|
||||
start "GaC GO gameserver" cmd /k gameserver-go.exe
|
||||
echo Both servers started. Close their windows to stop them.
|
||||
pause
|
||||
goto :eof
|
||||
|
||||
:gs
|
||||
gameserver-go.exe
|
||||
pause
|
||||
goto :eof
|
||||
|
||||
:sdk
|
||||
sdkserver-go.exe
|
||||
pause
|
||||
goto :eof
|
||||
|
||||
:build
|
||||
where go >nul 2>nul || set "PATH=%PATH%;C:\Program Files\Go\bin"
|
||||
go build -o gameserver-go.exe ./cmd/gameserver || (pause & goto :eof)
|
||||
go build -o sdkserver-go.exe ./cmd/sdkserver || (pause & goto :eof)
|
||||
echo Build OK.
|
||||
pause
|
||||
goto :eof
|
||||
call run.bat %*
|
||||
@@ -0,0 +1,131 @@
|
||||
-- ==============================================================================
|
||||
-- Hide UI Lua Script for Honkai: Star Rail (FFGO Method)
|
||||
-- Press "ฃ" (Backslash '\' on keyboard) to toggle UI visibility
|
||||
-- Uses /UIRoot/Page CanvasGroup alpha - character rendering stays completely intact!
|
||||
-- ==============================================================================
|
||||
|
||||
local Input = CS.UnityEngine.Input
|
||||
local KeyCode = CS.UnityEngine.KeyCode
|
||||
local Time = CS.UnityEngine.Time
|
||||
local GameObject = CS.UnityEngine.GameObject
|
||||
local CanvasGroup = CS.UnityEngine.CanvasGroup
|
||||
local Camera = CS.UnityEngine.Camera
|
||||
|
||||
_G.__HIDE_UI_ENABLED = _G.__HIDE_UI_ENABLED or false
|
||||
_G.__HIDE_UI_LAST_FRAME = _G.__HIDE_UI_LAST_FRAME or 0
|
||||
|
||||
-- Ensure all cameras and canvases are restored if previously disabled
|
||||
pcall(function()
|
||||
local allCams = Camera.allCameras
|
||||
if allCams and allCams.Length > 0 then
|
||||
for i = 0, allCams.Length - 1 do
|
||||
local c = allCams[i]
|
||||
if c and (c.name == "UICamera" or string.find(c.name, "UI") or string.find(c.name, "ui")) then
|
||||
c.enabled = true
|
||||
end
|
||||
end
|
||||
end
|
||||
local canvases = GameObject.FindObjectsOfType(typeof(CS.UnityEngine.Canvas))
|
||||
if canvases and canvases.Length > 0 then
|
||||
for i = 0, canvases.Length - 1 do
|
||||
if canvases[i] then canvases[i].enabled = true end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local function ensure_canvas_and_set_alpha(obj, visible)
|
||||
if not obj then return end
|
||||
pcall(function()
|
||||
local cg = obj:GetComponent(typeof(CanvasGroup))
|
||||
if not cg then
|
||||
cg = obj:AddComponent(typeof(CanvasGroup))
|
||||
end
|
||||
if cg then
|
||||
cg.alpha = visible and 1.0 or 0.0
|
||||
cg.interactable = true
|
||||
cg.blocksRaycasts = true
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function set_canvasgroup_visible(obj, visible)
|
||||
if not obj then return end
|
||||
pcall(function()
|
||||
local cg = obj:GetComponent(typeof(CanvasGroup))
|
||||
if cg then
|
||||
cg.alpha = visible and 1.0 or 0.0
|
||||
cg.interactable = true
|
||||
cg.blocksRaycasts = true
|
||||
end
|
||||
|
||||
for i = 0, obj.transform.childCount - 1 do
|
||||
local child = obj.transform:GetChild(i).gameObject
|
||||
local childCg = child:GetComponent(typeof(CanvasGroup))
|
||||
if childCg then
|
||||
childCg.alpha = visible and 1.0 or 0.0
|
||||
childCg.interactable = true
|
||||
childCg.blocksRaycasts = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function set_ui_visible(visible)
|
||||
pcall(function()
|
||||
local gameUI = GameObject.Find("/UIRoot/Page") or GameObject.Find("UIRoot/Page")
|
||||
if gameUI then
|
||||
ensure_canvas_and_set_alpha(gameUI, visible)
|
||||
local battleGameUI = GameObject.Find("/UIRoot/Page/BattleGamePhaseUI(Clone)") or GameObject.Find("UIRoot/Page/BattleGamePhaseUI(Clone)")
|
||||
if battleGameUI then
|
||||
ensure_canvas_and_set_alpha(battleGameUI, visible)
|
||||
set_canvasgroup_visible(battleGameUI, visible)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function _G.__HIDE_UI_TICK()
|
||||
pcall(function()
|
||||
local curFrame = Time.frameCount
|
||||
if _G.__HIDE_UI_LAST_FRAME == curFrame then
|
||||
return
|
||||
end
|
||||
|
||||
-- Check physical key 'ฃ' on keyboard (KeyCode.Backslash)
|
||||
if Input.GetKeyDown(KeyCode.Backslash) then
|
||||
_G.__HIDE_UI_LAST_FRAME = curFrame
|
||||
_G.__HIDE_UI_ENABLED = not _G.__HIDE_UI_ENABLED
|
||||
|
||||
set_ui_visible(not _G.__HIDE_UI_ENABLED)
|
||||
|
||||
if _G.__HIDE_UI_ENABLED then
|
||||
CS.UnityEngine.Debug.Log("<color=#FF00FF>[UI Toggle]</color> <color=#FF5555>UI Hidden (ซ่อน UI แล้ว)</color>")
|
||||
else
|
||||
CS.UnityEngine.Debug.Log("<color=#FF00FF>[UI Toggle]</color> <color=#55FF55>UI Restored (แสดง UI ตามปกติ)</color>")
|
||||
end
|
||||
end
|
||||
|
||||
-- When hidden, keep enforcing /UIRoot/Page visibility
|
||||
if _G.__HIDE_UI_ENABLED then
|
||||
set_ui_visible(false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Hook into render pipeline
|
||||
if not _G.__HIDE_UI_REGISTERED then
|
||||
_G.__HIDE_UI_REGISTERED = true
|
||||
|
||||
local function hook(target, evt)
|
||||
pcall(function() target[evt]('+', function(c) _G.__HIDE_UI_TICK() end) end)
|
||||
pcall(function() target[evt] = target[evt] + function(c) _G.__HIDE_UI_TICK() end end)
|
||||
end
|
||||
|
||||
hook(Camera, "onPreRender")
|
||||
hook(Camera, "onPreCull")
|
||||
hook(Camera, "onPostRender")
|
||||
pcall(function() CS.UnityEngine.Application.onBeforeRender('+', function() _G.__HIDE_UI_TICK() end) end)
|
||||
pcall(function() CS.UnityEngine.Application.onBeforeRender = CS.UnityEngine.Application.onBeforeRender + function() _G.__HIDE_UI_TICK() end end)
|
||||
|
||||
CS.UnityEngine.Debug.Log("<color=#FF00FF>[UI Toggle Script]</color> Loaded successfully (FFGO method)! Press 'ฃ' to toggle UI.")
|
||||
end
|
||||
@@ -0,0 +1,88 @@
|
||||
# บันทึกความจำ 2026-10-10: แก้ไข Endgame Modes ทั้ง 7 ข้อ (4.6.51)
|
||||
|
||||
## ปัญหาที่พบและวิธีแก้ไขครบทั้ง 7 ข้อ
|
||||
|
||||
### ข้อ 1: กด Apocalyptic Shadow ในแท็บ Latest Update แล้วขึ้น Error(100)
|
||||
- **สาเหตุ:** Client ส่ง cmd `1775` (`GetChallengeGroupStatisticsCsReq`) ซึ่งโค้ดเดิมใช้ byte slicing แบบ 4.5.52 เก่าที่มี tag ผิดเพี้ยน (`group_id=9`, `retcode=15`, `challenge_boss=11`) ทำให้ client 4.6.51 parse ไม่ได้แล้วโยน Error(100)
|
||||
- **การแก้ไข:** ใน `internal/gameserver/handlers_raw.go` เขียน `challengeGroupStatsReply` ใหม่โดยใช้ `goproto.GetChallengeGroupStatisticsScRsp` ตาม proto 4.6.51 โดยตรง:
|
||||
- `group_id = 5`
|
||||
- `retcode = 9` (0 = สำเร็จ)
|
||||
- AS: `challenge_boss = 1`
|
||||
- PF: `challenge_story = 3`
|
||||
- MoC: `challenge_default = 11`
|
||||
|
||||
### ข้อ 2: AS Starward (cmd 8986 `StartChallengeTierceCsReq`, challenge 30225) เข้าไม่ได้
|
||||
- **สาเหตุ:**
|
||||
1. `mapChallengeStageID` ฝืน map id 30200..30300 ทุกตัวไปเป็น 30204 (ซึ่ง 30204 ไม่มีใน `tierce`)
|
||||
2. `decodeStartChallengeTierceReq` ตรวจหาเฉพาะ tag 12/14 (4.5.52) แต่ client 4.6.51 ส่ง `LineupTierceList` ใน tag 9
|
||||
3. `handleStartChallengeTierce` ตรวจสอบ `cd.Tierce` เฉพาะเมื่อ `stageIndex == 2` ทำให้ stageIndex 0 หลุดไปหา 3014101 -> 3000101
|
||||
4. Response ตอบด้วย cmd 8973 (4.5.52) แทนที่จะเป็น 8998 (4.6.51)
|
||||
- **การแก้ไข:**
|
||||
1. `mapChallengeStageID` คืนค่า id ทันทีถ้า id มีอยู่ใน `cd.Challenges` หรือ `cd.Tierce` (30225 คืน 30225)
|
||||
2. `decodeStartChallengeTierceReq` parse ผ่าน `proto.Unmarshal(&goproto.StartChallengeTierceCsReq)`
|
||||
3. `handleStartChallengeTierce` ดึงข้อมูลจาก `cd.Tierce[baseID]` ทุก stageIndex ได้ entrance `3012602`, group `7`, monster `2034012`, event `420554`
|
||||
4. ตอบด้วย `goproto.StartChallengeTierceScRsp` ผ่าน CmdID **8998** (และ legacy 8973)
|
||||
|
||||
### ข้อ 3: AS Normal (cmd 1731 `StartChallengeCsReq`, challenge 30224) เข้าไม่ได้
|
||||
- **สาเหตุ:**
|
||||
1. `mapChallengeStageID` ฝืน map 30224 -> 30204
|
||||
2. `decodeStartChallengeReq` มี wire slicing bug (`buf = buf[len(buf)-len(sub):]; buf = buf[len(sub):]`) ทำให้ buf desync และพลาด challenge_id
|
||||
- **การแก้ไข:**
|
||||
1. `mapChallengeStageID` เช็ค `cd.Challenges[30224]` แล้วคืน 30224 ตรงๆ
|
||||
2. `decodeStartChallengeReq` ใช้ `proto.Unmarshal(&goproto.StartChallengeCsReq)` + helper `readLenDelimAndAdvance`
|
||||
3. `handleStartChallenge` ตอบ `StartChallengeScRsp` (1787) ครบทั้ง 2 Node
|
||||
|
||||
### ข้อ 4: ออกจาก Endgame แล้วตัวละครหาย
|
||||
- **สาเหตุ:**
|
||||
1. `loadChallengeScene` ไปเขียนทับ `player.Scene.EntryID = entryID` ทำให้ open world กลายเป็นฉาก arena (3000501/3000101) และถูกบันทึก persistent
|
||||
2. `handleLeaveChallenge` และ `handleLeaveChallengeTierce` ส่งเฉพาะ ScRsp เก่า (1716, 8997) โดยไม่ส่ง `loadScene` เพื่อพาผู้เล่นกลับ open world
|
||||
- **การแก้ไข:**
|
||||
1. นำบรรทัดเขียนทับ `player.Scene` ออกจาก `handlers_challenge_scene.go`
|
||||
2. `handleLeaveChallenge` ตอบ CmdID **1771** (`LeaveChallengeScRsp`, tag 10 retcode 0 `[0x50, 0x00]`) แล้วเรียก `loadScene(s, returnEntry, true, nil, nil)` สั่ง `EnterSceneByServerScNotify` (1427) ดึงตัวละครกลับฉากเดิม
|
||||
3. `handleLeaveChallengeTierce` ตอบ CmdID **8983** (`LeaveChallengeTierceScRsp`, tag 13 retcode 0 `[0x68, 0x00]`) แล้วเรียก `loadScene` เช่นกัน
|
||||
|
||||
### ข้อ 5: PF Starward หมุนค้างตอนจัดทีมตัวละคร
|
||||
- **สาเหตุ:** Client ส่ง cmd `8978` / `9000` (`SetChallengeTierceLineupCsReq`) ซึ่ง callback ใน GameAssembly.dll (`OMOEFAHFJFL::EMMGHDLIPOH`) รอ CmdID **8985** (`SetChallengeTierceLineupScRsp` / `CmdChallengeTierceType_PMMCIBMKOLD`) แต่เซิร์ฟเวอร์ส่ง 8999 ทำให้ client ไม่เคยได้รับ callback และหมุนค้าง
|
||||
- **การแก้ไข:** ใน `handleSetChallengeTierceLineup` ส่ง CmdID **8985** ด้วยบอดี้ `[0x08, 0x00]` (tag 1 retcode 0) และแมพทั้ง 8978 และ 9000 ใน `rawHandlers`
|
||||
|
||||
### ข้อ 6: PF Normal (challenge 20284) เข้าไม่ได้
|
||||
- **สาเหตุ:**
|
||||
1. `mapChallengeStageID` map 20284 -> 20254
|
||||
2. `handleStartChallenge` ส่ง `stage_info` (tag 13) เป็นก้อนว่างสำหรับโหมด PF ทำให้ client เกิดปัญหา
|
||||
- **การแก้ไข:**
|
||||
1. ไม่ map 20284 เพราะมีใน `cd.Challenges[20284]`
|
||||
2. `stage_info` (tag 13) จะแนบเฉพาะเมื่อ `challengeID >= 30000 && buff != 0` (AS Boss เท่านั้น) ส่วน MoC และ PF จะเป็น nil (ไม่ปล่อย tag 13)
|
||||
|
||||
### ข้อ 7: MoC Normal (challenge 5612) ค้างหน้าโหลดเหลือ 1 ขีด
|
||||
- **สาเหตุ:**
|
||||
1. ทันทีที่เข้าฉาก arena client ส่ง cmd `8115` (`GetSwitchHandDataCsReq`) แต่เซิร์ฟเวอร์ตอบด้วย cmd `8104` (ซึ่งเป็น CsReq: `SwitchHandStartCsReq`) ทำให้ client สับสนและค้าง loading notch สุดท้าย
|
||||
2. `mapChallengeStageID` มีการเพี้ยน id 5612
|
||||
- **การแก้ไข:**
|
||||
1. แก้ cmd `8115` ให้ตอบ CmdID **8108** (`GetSwitchHandDataScRsp`, body `[0x18, 0x00]`)
|
||||
2. `mapChallengeStageID` คืน 5612 ตรงๆ จาก `cd.Challenges[5612]` (entrance 3013801)
|
||||
|
||||
### ข้อ 8: ค้างหน้าจอโหลด 100% (รูปรถไฟ Astral Express หมุนไม่ไปต่อ)
|
||||
- **สาเหตุจาก Player.log:**
|
||||
1. **DrinkMaker NRE:** Client ส่ง cmd `6995` (`GetDrinkMakerDataCsReq`) แต่ `dispatch.go` ตอบกลับด้วย `6988` payload เปล่า (`len: 0`) เพราะไม่อยู่ใน `dangerFamilies` ส่งผลให้ `DrinkMakerBar._UpdateGameplayData()` ใน Unity client เจอก้อน nil แล้ว crash `NullReferenceException`
|
||||
2. **MapNPC Astral Express NRE:** บันทึก `persistent` ถูกตั้งไว้ที่ `entry_id: 100000104` (Astral Express Parlor Car `P10000_F10000000`) ซึ่งใน client 4.6.51 นี้ ไฟล์ config ของ Chapter00 มีปัญหา (`TerrainStageLodConfig ... 没找到`) และ `MapNPC:227-400001` ไม่มี ConfigEntityPath ทำให้ `AsyncLoad MapObject` โยน `NullReferenceException` ระหว่างขั้นตอน `AdventurePhase._EnterStep2()` ค้างโหลด 100% แล้วแอปเด้งปิด
|
||||
- **การแก้ไข:**
|
||||
1. เพิ่ม `"DrinkMaker"` ลงใน `dangerFamilies` และเพิ่ม CmdID `6995, 6988` ใน `isDangerCmd` (`dispatch.go`) ให้ drop เงียบๆ ป้องกัน empty ScRsp
|
||||
2. เปลี่ยน fallback ของ `GetCurSceneInfo` ใน `handlers_scene.go` และ `handleLeaveChallenge` / `handleLeaveChallengeTierce` ใน `handlers_challenge_flow.go` จาก `100000104` เป็น `102020107` (Belobog Administrative District `P10202_F10202001`) ซึ่งเป็นแมพสมบูรณ์ 100%
|
||||
3. รีเซ็ตไฟล์ `persistent` กลับเป็นแมพ `102020107` พิกัด `x: -92, y: 4278, z: 68083, rot_y: 155566`
|
||||
### ข้อ 9: Run.bat เปิดไม่ติด (หน้าต่างกระพริบปิดทันที)
|
||||
- **สาเหตุ:**
|
||||
1. ในคำสั่ง `if (...)` ของไฟล์ batch มีข้อความ `echo ... (Option 4).` ซึ่งตัวอักษร `)` ไปปิดบล็อก `if` ก่อนกำหนด ทำให้เครื่องหมายจุด `.` กลายเป็นคำสั่งที่ผิดไวยากรณ์ DOS/Windows (`. was unexpected at this time.`) ส่งผลให้ cmd แครชปิดหน้าต่างทันทีตั้งแต่บรรทัดแรกๆ
|
||||
2. การใช้ข้อความภาษาไทยในไฟล์ .bat ที่บันทึกแบบ UTF-8 ทำให้ cmd.exe ใน codepage เริ่มต้นของ Windows ตีความไบต์ผิดพลาด
|
||||
- **การแก้ไข:**
|
||||
1. แก้ไข `run.bat` และ `run_go.bat` ให้เป็น ASCII ที่สะอาด ไม่มีวงเล็บซ้อนในบล็อก `if`
|
||||
2. เพิ่มตัวเลือกแบบยืดหยุ่น:
|
||||
- [1] เปิด Server + ตัวเกม / Proxy (Default)
|
||||
- [2] เปิด Server ทั้งสองตัว (SDK + Game Server)
|
||||
- [3] เปิดเฉพาะ **SDK Server ONLY** (HTTP 21000)
|
||||
- [4] เปิดเฉพาะ **Game Server ONLY** (UDP 23301)
|
||||
- [5] เปิด Server + Gay Proxy
|
||||
- [6] Build / Rebuild binaries (Go)
|
||||
- [7] ออก
|
||||
3. ถ้าเปิดสำเร็จโดยไม่มี error จะนับถอยหลัง 5 วินาทีแล้วปิดหน้าต่าง Launcher ไปเองตามที่ผู้ใช้ต้องการ (และหากมี error จะ pause ค้างไว้ให้อ่าน)
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# บันทึกความจำ 2026-10-11: แก้ไขปัญหา Endgame 5 ข้อ (AS Starward ตกแมพ / Node 2 ค้าง / AS Normal / ตัวละครหาย / รีล็อกค้าง)
|
||||
|
||||
## ปัญหาที่พบและวิธีแก้ไขครบทั้ง 5 ข้อ
|
||||
|
||||
### 1. AS Starward (challenge 30225) เข้าแล้วตัวละครอยู่ใต้แมพ (ตก Void)
|
||||
- **สาเหตุ:**
|
||||
1. ใน `res.json` ของฉาก `3012602` (`P30126_F30126001`) มอนสเตอร์บอสใน Group 7 มีพิกัด `Y = -193081` (-193.08 เมตร) แต่วัตถุพื้นเวทีอารีน่า (Prop `102259` ใน Group 2) อยู่ที่ `Y = -170770` (-170.77 เมตร) ซึ่งพื้นเวทีอยู่สูงกว่ามอนสเตอร์ถึง 22.3 เมตร
|
||||
2. โค้ดเดิมใน `loadChallengeScene` กำหนด `spawnY = monsPos.Y` (-193081) ส่งผลให้ตัวละครเกิดใต้พื้นเวทีกระเด็นตก Void
|
||||
3. นอกจากนี้มีเงื่อนไข `if gid != 1 && gid != targetGroupID && gid != 7 && gid != 8 { continue }` ทำให้ Group 2 (แผ่นพื้นเวที) ไม่ถูกโหลดส่งให้ Client เลย
|
||||
- **การแก้ไข:**
|
||||
1. ใน `loadChallengeScene` (`internal/gameserver/handlers_challenge_scene.go`) เพิ่มระบบตรวจหาความสูงพื้นเวทีจาก Props ในฉาก (`hasFloorProp && floorY > monsPos.Y`) เพื่อปรับความสูงของทั้งผู้เล่นและมอนสเตอร์ให้ยืนบนพื้นเวทีจริง (`Y = -170770`)
|
||||
2. ยกเลิกการกรอง Group เพื่อให้ Props ของทุก Group (รวมถึงแผ่นพื้นเวที Group 2 และ Prop อารีน่า Group 3) ถูกส่งให้ Client ครบถ้วน
|
||||
|
||||
### 1.1 AS Starward เข้าห้องที่ 2 (Node 2) แล้วค้างหน้าโหลด
|
||||
- **สาเหตุ:**
|
||||
1. เมื่อผู้เล่นผ่านห้อง 1 และเข้าห้อง 2 (`req.stageIndex == 1`) โค้ดเดิมดึงข้อมูลจาก `cd.Tierce[baseID]` ซ้ำ ซึ่งในไฟล์ `challenge_data.json` ก้อน `tierce[30225]` มีเฉพาะข้อมูลห้อง 1 ทำให้เซิร์ฟเวอร์พยายามโหลดห้อง 1 ซ้ำที่เพิ่งจบไป
|
||||
2. ใน `handleStartChallengeTierce` มีการส่ง CmdID `8973` ซึ่งเป็น Packet โบราณที่ไม่มีใน Protobuf 4.6.51 ตามหลัง CmdID 8998 ทันที ทำให้ Network stream desync และ Coroutine โหลดฉากของ Client ค้าง
|
||||
- **การแก้ไข:**
|
||||
1. ใน `handleStartChallengeTierce` (`internal/gameserver/handlers_challenge_flow.go`) เมื่อ `req.stageIndex == 1` ให้ดึงข้อมูลด่านที่สองจาก `cd.Challenges[baseID-1]` (เช่น 30224 ห้อง 2: `entrance2: 3013201`, `group2: 9`, `monster2: 4014036`, `event2: 420574`) พร้อมเลือกชุดตัวละคร `req.secondAvatars`
|
||||
2. ตัดการส่ง CmdID `8973` ทิ้งทั้งหมด เหลือเฉพาะ `StartChallengeTierceScRsp` (CmdID **8998**) ตามมาตรฐาน 4.6.51
|
||||
|
||||
### 2. AS แบบปกติ (challenge 30214) ยังเข้าไม่ได้
|
||||
- **สาเหตุ:**
|
||||
1. ใน `handleStartChallenge` มีการแนบฟิลด์ `stage_info` (Tag 13) ไปใน `StartChallengeScRsp` (CmdID 1787) ซึ่งมีเฉพาะ `BuffId` แต่ขาดข้อมูล Lineup / Relics ที่ `BossChallengeModule` ของ Client คาดหวัง ส่งผลให้ Client ปฏิเสธการเข้าฉาก
|
||||
2. ในฉาก `3000205` มี Prop เพียงตัวเดียวอยู่ใน Group 3 ซึ่งถูกตัวกรองเดิมข้ามไป ทำให้ฉากไม่มี Prop ส่งไปเลย
|
||||
- **การแก้ไข:**
|
||||
1. ตัด `stage_info` ออกจาก `StartChallengeScRsp` ให้ส่งเฉพาะ `retcode`, `cur_challenge`, `lineup_list`, และ `scene` เหมือน MoC/PF ที่ทำงานได้ปกติ
|
||||
2. ปลดล็อกการโหลด Prop ทุก Group ทำให้ Prop 300002 ใน Group 3 ถูกโหลดสมบูรณ์
|
||||
|
||||
### 3, 4, 5. ออกจาก Endgame ตัวละครหาย, ไม่มีจุดวาปขึ้น, รีล็อกแล้วค้างหน้าโหลด
|
||||
- **สาเหตุ:**
|
||||
1. ในไฟล์ `persistent` มีการบันทึกฉากไว้ที่ `entry_id: 1030402` (Xianzhou Luofu Chapter 04) พิกัด `x: 780, y: -153008, z: -621450` ซึ่งเป็นพิกัดตก Void ลึก -153 เมตร
|
||||
2. เมื่อกดออกจาก Endgame ฟังก์ชัน `handleLeaveChallenge` และ `handleLeaveChallengeTierce` ตรวจพบว่าฉากไม่ใช่ 3000000..4000000 จึงพาผู้เล่นกลับไปที่ `1030402` พิกัด Void เดิม ทำให้ตัวละครตกแมพและมองไม่เห็น (ตัวละครหาย)
|
||||
3. Client 4.6.51 มีปัญหาไฟล์ Terrain LOD ของ Chapter 04 ขาดหาย (`TerrainStageLodConfig Stages/Outputs/Chapter04/... 没找到`) ทำให้ Coroutine ของฉากค้าง ส่งผลให้ UI แผนที่ไม่แสดงจุดวาป และเมื่อปิดเปิดเกมใหม่ `GetCurSceneInfo` ก็พยายามโหลด `1030402` ซ้ำจนค้างหน้าโหลด 100%
|
||||
- **การแก้ไข:**
|
||||
1. ใน `handleLeaveChallenge` และ `handleLeaveChallengeTierce` บังคับให้การออกจาก Endgame ส่งผู้เล่นกลับสู่เขตบริหาร Belobog Administrative District (`entry_id: 102020107`, `plane_id: 10202`, `floor_id: 10202001`, พิกัด `x: -92, y: 4278, z: 68083, rot_y: 155566`) เสมอ พร้อมเรียก `refreshLineup` เพื่อแสดงโมเดลตัวละครและรีเฟรชจุดวาปทั้งหมด
|
||||
2. ใน `GetCurSceneInfo` (`handlers_scene.go`) เพิ่มเงื่อนไขตรวจสอบ `entryID == 1030402 || player.Position.Y < -50000` หากพบฉากเสียหรือพิกัด Void ให้กู้คืนกลับมา Belobog `102020107` ทันทีและเซฟ persistent อัตโนมัติ ป้องกันการค้างตอนล็อกอิน
|
||||
3. รีเซ็ตไฟล์ `persistent` กลับมายัง Belobog `102020107`
|
||||
@@ -0,0 +1,33 @@
|
||||
# บันทึกความจำ 2026-10-16: แก้ไขปัญหา Calyx (ดอกไม้) เข้าต่อสู้ไม่ได้ / Network Flood Loop / REST API Gateway สำหรับ GaC-Srtool
|
||||
|
||||
## 1. REST API Gateway (port 21000)
|
||||
- นำหน้าเว็บ HTML Dashboard ภายในออกตามความต้องการของผู้ใช้ เพื่อเตรียมรองรับ GaC-Srtool ภายนอก
|
||||
- สร้าง REST API Gateway (`internal/sdkserver/api.go`) พร้อม CORS Header:
|
||||
- `GET /` และ `GET /dashboard`: ดูรายการ API ทั้งหมด
|
||||
- `GET, POST /api/freesr`: อ่าน/เขียน `freesr-data.json` ทั้งหมด
|
||||
- `GET, POST /api/freesr/avatars`: จัดการเลเวล, ปลดดาว, รีลิกส์, Ex-Rank
|
||||
- `GET, POST /api/persistent`: อ่าน/เขียนไฟล์ persistent
|
||||
- `POST /api/exrank/toggle`: เปิด/ปิด Ex-Rank รายตัวหรือทุกตัว
|
||||
- `POST /api/avatar/rank`: ปรับ Eidolon (0-6)
|
||||
- `POST /api/avatar/max_all`: แม็กซ์ตัวละครทุกตัว
|
||||
- `POST /api/teleport`: วาปตำแหน่ง/ฉาก
|
||||
- `GET /api/status`: สรุปสถานะเซิร์ฟเวอร์, จำนวนตัวละคร, ทีมปัจจุบัน
|
||||
|
||||
## 2. แก้ปัญหา Calyx (ดอกไม้) เข้าไม่ได้ และ Network Flood Loop
|
||||
- **สาเหตุที่ 1 (Network Flood Loop & Battle Reset):**
|
||||
- ใน `handlers_scene.go` ที่ฟังก์ชัน `SceneEntityMove` มีการเรียก `player.SavePersistent()` ทุกเฟรมที่มีการเคลื่อนที่ของผู้เล่น
|
||||
- ใน `gateway.go` ฟังก์ชัน `watchFreesrData` เฝ้าดูการเปลี่ยนแปลงของไฟล์ `persistent` ทุก 1 วินาที
|
||||
- เมื่อผู้เล่นขยับ `persistent` ถูกเขียนซ้ำตลอดเวลา ทำให้ `watchFreesrData` สั่ง `session.SyncPlayer()` ทุกวินาที
|
||||
- `SyncPlayer()` ยิงแพ็กเก็ต `PlayerSyncScNotify` ขนาดกว่า 60KB (ประกอบด้วย `DelRelicList`, `DelEquipmentList`, `AvatarSync`) ล้างไอเทมและรีเซ็ตตัวละครกลับมาเป็นสถานะ Overworld ขัดจังหวะการโหลดเข้าสู่การต่อสู้ Calyx
|
||||
- **สาเหตุที่ 2 (NullReferenceException ใน `GetCurBattleInfo`):**
|
||||
- ใน `handlers_battle.go` ฟังก์ชัน `GetCurBattleInfo` มีโค้ด fallback ปลอมที่สร้าง battleInfo ส่งกลับด้วย retcode 0 แม้ไม่ได้อยู่ในการต่อสู้
|
||||
- ทำให้ Client เมื่อเรียก `GetCurBattleInfoCsReq` เกิด crash NRE ใน `AdventureModule._OnGetCurBattleInfoScRsp` (RVA `0xceb7e65`) เนื่องจาก Battle Module ภายในยังไม่ถูก initialize
|
||||
- **สาเหตุที่ 3 (การตัดและบิดเบือน MonsterWaveList ใน Calyx):**
|
||||
- ใน `StartCocoonStage` และ `StartQuickCocoonStage` มีการบังคับตัดเวฟ `MonsterWaveList[:1]` และเซ็ต `w.WaveId = 1` ให้กับทุกเวฟ ทำให้โครงสร้างเวฟของ Client เสียหาย
|
||||
- **การแก้ไข:**
|
||||
1. แก้ `gateway.go`: ให้ `watchFreesrData` เฝ้าดูเฉพาะ `freesr-data.json` เท่านั้น (ไม่ดู `persistent`) เหมือนโครงสร้างต้นฉบับ
|
||||
2. แก้ `handlers_scene.go`: นำ `player.SavePersistent()` ออกจาก `SceneEntityMove` เพื่อหยุดการเขียนดิสก์และลูป Network flood โดยสิ้นเชิง
|
||||
3. แก้ `handlers_battle.go`:
|
||||
- ใน `GetCurBattleInfo` ตัด fallback ปลอมออก หากไม่มี Token ใน `CURRENT_BATTLE` ให้ตอบ `RET_BATTLE_STAGE_NOT_MATCH` (2000) ป้องกัน NRE
|
||||
- ใน `StartCocoonStage` และ `StartQuickCocoonStage` ส่ง `battleInfo` ที่สมบูรณ์พร้อมเวฟที่ถูกต้อง และเพิ่ม Log แสดงค่า `CocoonId`, `Wave`, `WorldLevel`
|
||||
4. คอมไพล์ `gameserver-go.exe` ใหม่และรีสตาร์ทกระบวนการ
|
||||
@@ -64,7 +64,7 @@ description: คู่มือ Honkai Star Rail Private Server (GaC-hkrpg-go
|
||||
- **GetNpcTakenReward (2174)**: client ถามก่อนคุย NPC — ไม่ตอบ = dialogue ไม่มีตัวเลือกและค้าง
|
||||
- **Tutorial**: mark ทุก id ว่าดูแล้ว (`GetTutorial`/`GetTutorialGuide` status enum `Oekdmamgidc`=2) ไม่งั้น popup เด้งวนและ**บล็อก input**; `FinishTutorial/UnlockTutorial*` ต้องตอบพร้อม Tutorial/TutorialGuide message
|
||||
- **GetMissionStatusScRsp ต้องส่ง `finished_main_mission_id_list` + `curversion_finished_main_mission_id_list` (tag 13)** — ตอบเปล่า = client Lua NRE → บล็อก input + client ส่ง cmd 1204 ซ้ำรัวๆ
|
||||
- **GetSwitchHandData**: 4.6.51 CsReq 8115 → ตอบ ScRsp **8104** (tag 3 retcode 0)
|
||||
- **GetSwitchHandData**: 4.6.51 CsReq 8115 → ตอบ ScRsp **8108** (tag 3 retcode 0; 8104 คือ SwitchHandStartCsReq ห้ามตอบ)
|
||||
- **arena ใหม่ไม่มีใน client เก่า**: challenge phase ใหม่ (เช่น 5312) ใช้ arena 3014101 ซึ่ง client build เก่าไม่มี MapEntrance → Lua `GotoMapParam: attempt to index nil with 'ID'` — fallback ไป arena มาตรฐาน 3000101
|
||||
- **Scene ไม่มี teleport**: ฉาก arena ไม่มี teleport ใน res — fallback จาก `teleports.json` หรือ spawn ใกล้ prop แรกหันหน้าเข้าหา
|
||||
- **full coverage ตอบ ScRsp ว่างทุก cmd = อันตราย!** — client modules (`GridFight`, `ChessRogue`, `EvolveBuild`, `ChimeraDuel`, `Jukebox`) รับว่างแล้ว `Sync(nil)` → NRE วน → **จอดำ** — cmd ไม่รู้จักต้อง **drop เงียบๆ** ตอบว่างได้เฉพาะ dummy list ที่ทดสอบแล้ว
|
||||
@@ -149,3 +149,22 @@ commit สุดท้ายของ Rust (`ebc27a1`) อ้างชื่อ
|
||||
Endgame entry รอ response นี้ก่อนเปิด UI (บันทึก Rust บอก 4130 ผิด)
|
||||
- **Activity schedule จริง**: `tools/march7_dump/Config/Custom/ActivityConfig.json`
|
||||
(147 activities จาก client 4.6.51) — อย่าส่ง synthetic id เพราะ Travel Log จะว่าง
|
||||
|
||||
## 14. บทเรียนระบบ Endgame ใน 4.6.51 (Apocalyptic Shadow, Pure Fiction, MoC)
|
||||
|
||||
1. **GetChallengeGroupStatistics (1775/1711 → 1708)**:
|
||||
- Client ส่ง `GetChallengeGroupStatisticsCsReq`
|
||||
- ScRsp 1708 ต้องใช้ proto 4.6.51 (`group_id=5`, `retcode=9`, `challenge_boss=1`, `challenge_story=3`, `challenge_default=11`) ห้ามใช้ layout 4.5.52
|
||||
2. **StartChallengeTierce (Starward/PF/AS) = CmdID 8998 (4.6.51)**:
|
||||
- Client ส่ง `StartChallengeTierceCsReq` (8986): `challenge_id` (tag 3), `stageIndex` (tag 5), `lineup_tierce_list` (tag 9)
|
||||
- Response ใน 4.6.51 คือ **8998** (`StartChallengeTierceScRsp`) ประกอบด้วย `retcode` (tag 3), `scene` (tag 9), `cur_tierce_challenge` (tag 15)
|
||||
- ข้อมูลแมพดึงจาก `cd.Tierce[baseID]` สำหรับทุก stageIndex
|
||||
3. **SetChallengeTierceLineup (8978/9000 → 8985)**:
|
||||
- Callback `OMOEFAHFJFL::EMMGHDLIPOH` ใน client รอ CmdID **8985** (`SetChallengeTierceLineupScRsp`) บอดี้ `[0x08, 0x00]`
|
||||
- เซิร์ฟเวอร์เดิมส่ง 8999 ทำให้ client หมุนค้างตลอดเวลาเลือกตัวละคร
|
||||
4. **Leave Challenge (1705 → 1771, 8989 → 8983) และการรักษาสภาพตัวละคร**:
|
||||
- `loadChallengeScene` **ห้ามเขียนทับ** `player.Scene.EntryID/FloorID/PlaneID` — มิฉะนั้น persistent save จะพังเป็นฉาก arena
|
||||
- เมื่อผู้เล่นกดยกเลิก/ออกจาก Endgame ต้องตอบ ScRsp (1771 หรือ 8983) พร้อมเรียก `loadScene(s, returnEntry, true, nil, nil)` สั่ง `EnterSceneByServerScNotify` (1427) ดึงตัวละครกลับ open world ทันที ตัวละครจะไม่หาย
|
||||
5. **StartChallenge Lineup List & Stage Info (1787)**:
|
||||
- ต้องแนบทั้ง Node 1 (index 0) และ Node 2 (index 1) ใน `LineupList`
|
||||
- `StageInfo` (tag 13) แนบเฉพาะเมื่อ `challengeID >= 30000 && buff != 0` (AS Boss) ห้ามแนบว่างใน MoC และ PF
|
||||
@@ -190,5 +190,29 @@
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16700702_5084ff364848_ebdcee37054070",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16679499_5abbd35a74c5_a28e83e88b78d5",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
|
||||
},
|
||||
"CNBETAWin4.6.52": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16745161_ab68020d7aa9_43440a8f18e051",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16755051_300cb1cd6a4f_c87db7a5d4f2bf",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16745283_0b6f44e69f46_3bf68a1c7368cd",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
|
||||
},
|
||||
"OSBETAWin4.6.52": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16745161_ab68020d7aa9_43440a8f18e051",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16755051_300cb1cd6a4f_c87db7a5d4f2bf",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16745283_0b6f44e69f46_3bf68a1c7368cd",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
|
||||
},
|
||||
"CNBETAWin4.6.53": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16818953_d4345bbd54a2_93c4aa5dfb978d",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16833202_959c316ef547_ac20bef3513343",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16819203_679727d45bd7_91ccc9b73f52b7",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
|
||||
},
|
||||
"OSBETAWin4.6.53": {
|
||||
"asset_bundle_url": "https://autopatchcn.bhsr.com/asb/BetaLive/output_16818953_d4345bbd54a2_93c4aa5dfb978d",
|
||||
"ex_resource_url": "https://autopatchcn.bhsr.com/design_data/BetaLive/output_16833202_959c316ef547_ac20bef3513343",
|
||||
"lua_url": "https://autopatchcn.bhsr.com/lua/BetaLive/output_16819203_679727d45bd7_91ccc9b73f52b7",
|
||||
"ifix_url": "https://autopatchcn.bhsr.com/ifix/BetaLive/output_0_40d2ce0253_c61ba99f70b885"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user