mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Compare commits
19 commits
3103662528
...
be4cbad0b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be4cbad0b0 | ||
|
|
b6047cdce8 | ||
|
|
f9856c1046 | ||
|
|
31ab76427f | ||
|
|
cbdf03c07e | ||
|
|
296121f5e7 | ||
|
|
73ce601bb5 | ||
|
|
5086520354 | ||
|
|
f29ebbdd71 | ||
|
|
6d72228f66 | ||
|
|
ab8ff6b7e5 | ||
|
|
e9ce062112 | ||
|
|
329a1f4b12 | ||
|
|
ce4f93dfcb | ||
|
|
1482694495 | ||
|
|
027640189a | ||
|
|
7565019dc9 | ||
|
|
bd725f0bbe | ||
|
|
572bb4ef36 |
18 changed files with 2195 additions and 349 deletions
|
|
@ -1,108 +1,256 @@
|
|||
{
|
||||
"Spell": {
|
||||
"ID": 0, "Attributes": 5, "AttributesEx": 6, "IconID": 117,
|
||||
"Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1,
|
||||
"CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33,
|
||||
"ID": 0,
|
||||
"Attributes": 5,
|
||||
"AttributesEx": 6,
|
||||
"IconID": 117,
|
||||
"Name": 120,
|
||||
"Tooltip": 147,
|
||||
"Rank": 129,
|
||||
"SchoolEnum": 1,
|
||||
"CastingTimeIndex": 15,
|
||||
"PowerType": 28,
|
||||
"ManaCost": 29,
|
||||
"RangeIndex": 33,
|
||||
"DispelType": 4
|
||||
},
|
||||
"SpellRange": { "MaxRange": 2 },
|
||||
"SpellRange": {
|
||||
"MaxRange": 2
|
||||
},
|
||||
"ItemDisplayInfo": {
|
||||
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||
"ID": 0,
|
||||
"LeftModel": 1,
|
||||
"LeftModelTexture": 3,
|
||||
"InventoryIcon": 5,
|
||||
"GeosetGroup1": 7,
|
||||
"GeosetGroup3": 9,
|
||||
"TextureArmUpper": 14,
|
||||
"TextureArmLower": 15,
|
||||
"TextureHand": 16,
|
||||
"TextureTorsoUpper": 17,
|
||||
"TextureTorsoLower": 18,
|
||||
"TextureLegUpper": 19,
|
||||
"TextureLegLower": 20,
|
||||
"TextureFoot": 21
|
||||
},
|
||||
"CharSections": {
|
||||
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||
"VariationIndex": 4, "ColorIndex": 5,
|
||||
"Texture1": 6, "Texture2": 7, "Texture3": 8,
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"BaseSection": 3,
|
||||
"VariationIndex": 4,
|
||||
"ColorIndex": 5,
|
||||
"Texture1": 6,
|
||||
"Texture2": 7,
|
||||
"Texture3": 8,
|
||||
"Flags": 9
|
||||
},
|
||||
"SpellIcon": { "ID": 0, "Path": 1 },
|
||||
"SpellIcon": {
|
||||
"ID": 0,
|
||||
"Path": 1
|
||||
},
|
||||
"FactionTemplate": {
|
||||
"ID": 0, "Faction": 1, "FactionGroup": 3,
|
||||
"FriendGroup": 4, "EnemyGroup": 5,
|
||||
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
|
||||
"ID": 0,
|
||||
"Faction": 1,
|
||||
"FactionGroup": 3,
|
||||
"FriendGroup": 4,
|
||||
"EnemyGroup": 5,
|
||||
"Enemy0": 6,
|
||||
"Enemy1": 7,
|
||||
"Enemy2": 8,
|
||||
"Enemy3": 9
|
||||
},
|
||||
"Faction": {
|
||||
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
|
||||
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
|
||||
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||
"ReputationBase2": 12, "ReputationBase3": 13
|
||||
"ID": 0,
|
||||
"ReputationRaceMask0": 2,
|
||||
"ReputationRaceMask1": 3,
|
||||
"ReputationRaceMask2": 4,
|
||||
"ReputationRaceMask3": 5,
|
||||
"ReputationBase0": 10,
|
||||
"ReputationBase1": 11,
|
||||
"ReputationBase2": 12,
|
||||
"ReputationBase3": 13
|
||||
},
|
||||
"AreaTable": {
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"ParentAreaNum": 2,
|
||||
"ExploreFlag": 3
|
||||
},
|
||||
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||
"CreatureDisplayInfoExtra": {
|
||||
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
|
||||
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
|
||||
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
|
||||
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
|
||||
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
|
||||
"ID": 0,
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"SkinID": 3,
|
||||
"FaceID": 4,
|
||||
"HairStyleID": 5,
|
||||
"HairColorID": 6,
|
||||
"FacialHairID": 7,
|
||||
"EquipDisplay0": 8,
|
||||
"EquipDisplay1": 9,
|
||||
"EquipDisplay2": 10,
|
||||
"EquipDisplay3": 11,
|
||||
"EquipDisplay4": 12,
|
||||
"EquipDisplay5": 13,
|
||||
"EquipDisplay6": 14,
|
||||
"EquipDisplay7": 15,
|
||||
"EquipDisplay8": 16,
|
||||
"EquipDisplay9": 17,
|
||||
"EquipDisplay10": 18,
|
||||
"BakeName": 20
|
||||
},
|
||||
"CreatureDisplayInfo": {
|
||||
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
|
||||
"Skin1": 6, "Skin2": 7, "Skin3": 8
|
||||
"ID": 0,
|
||||
"ModelID": 1,
|
||||
"ExtraDisplayId": 3,
|
||||
"Skin1": 6,
|
||||
"Skin2": 7,
|
||||
"Skin3": 8
|
||||
},
|
||||
"TaxiNodes": {
|
||||
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"X": 2,
|
||||
"Y": 3,
|
||||
"Z": 4,
|
||||
"Name": 5
|
||||
},
|
||||
"TaxiPath": {
|
||||
"ID": 0,
|
||||
"FromNode": 1,
|
||||
"ToNode": 2,
|
||||
"Cost": 3
|
||||
},
|
||||
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
|
||||
"TaxiPathNode": {
|
||||
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
|
||||
"X": 4, "Y": 5, "Z": 6
|
||||
"ID": 0,
|
||||
"PathID": 1,
|
||||
"NodeIndex": 2,
|
||||
"MapID": 3,
|
||||
"X": 4,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
},
|
||||
"TalentTab": {
|
||||
"ID": 0, "Name": 1, "ClassMask": 12,
|
||||
"OrderIndex": 14, "BackgroundFile": 15
|
||||
"ID": 0,
|
||||
"Name": 1,
|
||||
"ClassMask": 12,
|
||||
"OrderIndex": 14,
|
||||
"BackgroundFile": 15
|
||||
},
|
||||
"Talent": {
|
||||
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
|
||||
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
|
||||
"ID": 0,
|
||||
"TabID": 1,
|
||||
"Row": 2,
|
||||
"Column": 3,
|
||||
"RankSpell0": 4,
|
||||
"PrereqTalent0": 9,
|
||||
"PrereqRank0": 12
|
||||
},
|
||||
"SkillLineAbility": {
|
||||
"SkillLineID": 1,
|
||||
"SpellID": 2
|
||||
},
|
||||
"SkillLine": {
|
||||
"ID": 0,
|
||||
"Category": 1,
|
||||
"Name": 3
|
||||
},
|
||||
"Map": {
|
||||
"ID": 0,
|
||||
"InternalName": 1
|
||||
},
|
||||
"CreatureModelData": {
|
||||
"ID": 0,
|
||||
"ModelPath": 2
|
||||
},
|
||||
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
|
||||
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
|
||||
"Map": { "ID": 0, "InternalName": 1 },
|
||||
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
|
||||
"CharHairGeosets": {
|
||||
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"Variation": 3,
|
||||
"GeosetID": 4
|
||||
},
|
||||
"CharacterFacialHairStyles": {
|
||||
"RaceID": 0, "SexID": 1, "Variation": 2,
|
||||
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
|
||||
"RaceID": 0,
|
||||
"SexID": 1,
|
||||
"Variation": 2,
|
||||
"Geoset100": 3,
|
||||
"Geoset300": 4,
|
||||
"Geoset200": 5
|
||||
},
|
||||
"GameObjectDisplayInfo": {
|
||||
"ID": 0,
|
||||
"ModelName": 1
|
||||
},
|
||||
"Emotes": {
|
||||
"ID": 0,
|
||||
"AnimID": 2
|
||||
},
|
||||
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
|
||||
"Emotes": { "ID": 0, "AnimID": 2 },
|
||||
"EmotesText": {
|
||||
"ID": 0, "Command": 1, "EmoteRef": 2,
|
||||
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
|
||||
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
|
||||
"ID": 0,
|
||||
"Command": 1,
|
||||
"EmoteRef": 2,
|
||||
"OthersTargetTextID": 3,
|
||||
"SenderTargetTextID": 5,
|
||||
"OthersNoTargetTextID": 7,
|
||||
"SenderNoTargetTextID": 9
|
||||
},
|
||||
"EmotesTextData": {
|
||||
"ID": 0,
|
||||
"Text": 1
|
||||
},
|
||||
"EmotesTextData": { "ID": 0, "Text": 1 },
|
||||
"Light": {
|
||||
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
|
||||
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
|
||||
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"X": 2,
|
||||
"Z": 3,
|
||||
"Y": 4,
|
||||
"InnerRadius": 5,
|
||||
"OuterRadius": 6,
|
||||
"LightParamsID": 7,
|
||||
"LightParamsIDRain": 8,
|
||||
"LightParamsIDUnderwater": 9
|
||||
},
|
||||
"LightParams": {
|
||||
"LightParamsID": 0
|
||||
},
|
||||
"LightParams": { "LightParamsID": 0 },
|
||||
"LightIntBand": {
|
||||
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||
"BlockIndex": 1,
|
||||
"NumKeyframes": 2,
|
||||
"TimeKey0": 3,
|
||||
"Value0": 19
|
||||
},
|
||||
"LightFloatBand": {
|
||||
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||
"BlockIndex": 1,
|
||||
"NumKeyframes": 2,
|
||||
"TimeKey0": 3,
|
||||
"Value0": 19
|
||||
},
|
||||
"WorldMapArea": {
|
||||
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
|
||||
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
|
||||
"DisplayMapID": 8, "ParentWorldMapID": 10
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"AreaID": 2,
|
||||
"AreaName": 3,
|
||||
"LocLeft": 4,
|
||||
"LocRight": 5,
|
||||
"LocTop": 6,
|
||||
"LocBottom": 7,
|
||||
"DisplayMapID": 8,
|
||||
"ParentWorldMapID": 10
|
||||
},
|
||||
"SpellVisual": {
|
||||
"ID": 0, "CastKit": 2, "ImpactKit": 3, "MissileModel": 8
|
||||
"ID": 0,
|
||||
"CastKit": 2,
|
||||
"ImpactKit": 3,
|
||||
"MissileModel": 8
|
||||
},
|
||||
"SpellVisualKit": {
|
||||
"ID": 0, "BaseEffect": 5, "SpecialEffect0": 11, "SpecialEffect1": 12, "SpecialEffect2": 13
|
||||
"ID": 0,
|
||||
"BaseEffect": 5,
|
||||
"SpecialEffect0": 11,
|
||||
"SpecialEffect1": 12,
|
||||
"SpecialEffect2": 13
|
||||
},
|
||||
"SpellVisualEffectName": {
|
||||
"ID": 0, "FilePath": 2
|
||||
"ID": 0,
|
||||
"FilePath": 2
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,124 +1,303 @@
|
|||
{
|
||||
"Spell": {
|
||||
"ID": 0, "Attributes": 5, "AttributesEx": 6, "IconID": 124,
|
||||
"Name": 127, "Tooltip": 154, "Rank": 136, "SchoolMask": 215,
|
||||
"CastingTimeIndex": 22, "PowerType": 35, "ManaCost": 36, "RangeIndex": 40,
|
||||
"ID": 0,
|
||||
"Attributes": 5,
|
||||
"AttributesEx": 6,
|
||||
"IconID": 124,
|
||||
"Name": 127,
|
||||
"Tooltip": 154,
|
||||
"Rank": 136,
|
||||
"SchoolMask": 215,
|
||||
"CastingTimeIndex": 22,
|
||||
"PowerType": 35,
|
||||
"ManaCost": 36,
|
||||
"RangeIndex": 40,
|
||||
"DispelType": 3
|
||||
},
|
||||
"SpellRange": { "MaxRange": 4 },
|
||||
"SpellRange": {
|
||||
"MaxRange": 4
|
||||
},
|
||||
"ItemDisplayInfo": {
|
||||
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||
"ID": 0,
|
||||
"LeftModel": 1,
|
||||
"LeftModelTexture": 3,
|
||||
"InventoryIcon": 5,
|
||||
"GeosetGroup1": 7,
|
||||
"GeosetGroup3": 9,
|
||||
"TextureArmUpper": 14,
|
||||
"TextureArmLower": 15,
|
||||
"TextureHand": 16,
|
||||
"TextureTorsoUpper": 17,
|
||||
"TextureTorsoLower": 18,
|
||||
"TextureLegUpper": 19,
|
||||
"TextureLegLower": 20,
|
||||
"TextureFoot": 21
|
||||
},
|
||||
"CharSections": {
|
||||
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||
"VariationIndex": 4, "ColorIndex": 5,
|
||||
"Texture1": 6, "Texture2": 7, "Texture3": 8,
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"BaseSection": 3,
|
||||
"VariationIndex": 4,
|
||||
"ColorIndex": 5,
|
||||
"Texture1": 6,
|
||||
"Texture2": 7,
|
||||
"Texture3": 8,
|
||||
"Flags": 9
|
||||
},
|
||||
"SpellIcon": { "ID": 0, "Path": 1 },
|
||||
"SpellIcon": {
|
||||
"ID": 0,
|
||||
"Path": 1
|
||||
},
|
||||
"FactionTemplate": {
|
||||
"ID": 0, "Faction": 1, "FactionGroup": 3,
|
||||
"FriendGroup": 4, "EnemyGroup": 5,
|
||||
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
|
||||
"ID": 0,
|
||||
"Faction": 1,
|
||||
"FactionGroup": 3,
|
||||
"FriendGroup": 4,
|
||||
"EnemyGroup": 5,
|
||||
"Enemy0": 6,
|
||||
"Enemy1": 7,
|
||||
"Enemy2": 8,
|
||||
"Enemy3": 9
|
||||
},
|
||||
"Faction": {
|
||||
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
|
||||
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
|
||||
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||
"ReputationBase2": 12, "ReputationBase3": 13
|
||||
"ID": 0,
|
||||
"ReputationRaceMask0": 2,
|
||||
"ReputationRaceMask1": 3,
|
||||
"ReputationRaceMask2": 4,
|
||||
"ReputationRaceMask3": 5,
|
||||
"ReputationBase0": 10,
|
||||
"ReputationBase1": 11,
|
||||
"ReputationBase2": 12,
|
||||
"ReputationBase3": 13
|
||||
},
|
||||
"CharTitles": {
|
||||
"ID": 0,
|
||||
"Title": 2,
|
||||
"TitleBit": 20
|
||||
},
|
||||
"AreaTable": {
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"ParentAreaNum": 2,
|
||||
"ExploreFlag": 3
|
||||
},
|
||||
"CharTitles": { "ID": 0, "Title": 2, "TitleBit": 20 },
|
||||
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||
"CreatureDisplayInfoExtra": {
|
||||
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
|
||||
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
|
||||
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
|
||||
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
|
||||
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
|
||||
"ID": 0,
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"SkinID": 3,
|
||||
"FaceID": 4,
|
||||
"HairStyleID": 5,
|
||||
"HairColorID": 6,
|
||||
"FacialHairID": 7,
|
||||
"EquipDisplay0": 8,
|
||||
"EquipDisplay1": 9,
|
||||
"EquipDisplay2": 10,
|
||||
"EquipDisplay3": 11,
|
||||
"EquipDisplay4": 12,
|
||||
"EquipDisplay5": 13,
|
||||
"EquipDisplay6": 14,
|
||||
"EquipDisplay7": 15,
|
||||
"EquipDisplay8": 16,
|
||||
"EquipDisplay9": 17,
|
||||
"EquipDisplay10": 18,
|
||||
"BakeName": 20
|
||||
},
|
||||
"CreatureDisplayInfo": {
|
||||
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
|
||||
"Skin1": 6, "Skin2": 7, "Skin3": 8
|
||||
"ID": 0,
|
||||
"ModelID": 1,
|
||||
"ExtraDisplayId": 3,
|
||||
"Skin1": 6,
|
||||
"Skin2": 7,
|
||||
"Skin3": 8
|
||||
},
|
||||
"TaxiNodes": {
|
||||
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5,
|
||||
"MountDisplayIdAllianceFallback": 12, "MountDisplayIdHordeFallback": 13,
|
||||
"MountDisplayIdAlliance": 14, "MountDisplayIdHorde": 15
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"X": 2,
|
||||
"Y": 3,
|
||||
"Z": 4,
|
||||
"Name": 5,
|
||||
"MountDisplayIdAllianceFallback": 12,
|
||||
"MountDisplayIdHordeFallback": 13,
|
||||
"MountDisplayIdAlliance": 14,
|
||||
"MountDisplayIdHorde": 15
|
||||
},
|
||||
"TaxiPath": {
|
||||
"ID": 0,
|
||||
"FromNode": 1,
|
||||
"ToNode": 2,
|
||||
"Cost": 3
|
||||
},
|
||||
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
|
||||
"TaxiPathNode": {
|
||||
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
|
||||
"X": 4, "Y": 5, "Z": 6
|
||||
"ID": 0,
|
||||
"PathID": 1,
|
||||
"NodeIndex": 2,
|
||||
"MapID": 3,
|
||||
"X": 4,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
},
|
||||
"TalentTab": {
|
||||
"ID": 0, "Name": 1, "ClassMask": 12,
|
||||
"OrderIndex": 14, "BackgroundFile": 15
|
||||
"ID": 0,
|
||||
"Name": 1,
|
||||
"ClassMask": 12,
|
||||
"OrderIndex": 14,
|
||||
"BackgroundFile": 15
|
||||
},
|
||||
"Talent": {
|
||||
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
|
||||
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
|
||||
"ID": 0,
|
||||
"TabID": 1,
|
||||
"Row": 2,
|
||||
"Column": 3,
|
||||
"RankSpell0": 4,
|
||||
"PrereqTalent0": 9,
|
||||
"PrereqRank0": 12
|
||||
},
|
||||
"SkillLineAbility": {
|
||||
"SkillLineID": 1,
|
||||
"SpellID": 2
|
||||
},
|
||||
"SkillLine": {
|
||||
"ID": 0,
|
||||
"Category": 1,
|
||||
"Name": 3
|
||||
},
|
||||
"Map": {
|
||||
"ID": 0,
|
||||
"InternalName": 1
|
||||
},
|
||||
"CreatureModelData": {
|
||||
"ID": 0,
|
||||
"ModelPath": 2
|
||||
},
|
||||
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
|
||||
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
|
||||
"Map": { "ID": 0, "InternalName": 1 },
|
||||
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
|
||||
"CharHairGeosets": {
|
||||
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"Variation": 3,
|
||||
"GeosetID": 4
|
||||
},
|
||||
"CharacterFacialHairStyles": {
|
||||
"RaceID": 0, "SexID": 1, "Variation": 2,
|
||||
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
|
||||
"RaceID": 0,
|
||||
"SexID": 1,
|
||||
"Variation": 2,
|
||||
"Geoset100": 3,
|
||||
"Geoset300": 4,
|
||||
"Geoset200": 5
|
||||
},
|
||||
"GameObjectDisplayInfo": {
|
||||
"ID": 0,
|
||||
"ModelName": 1
|
||||
},
|
||||
"Emotes": {
|
||||
"ID": 0,
|
||||
"AnimID": 2
|
||||
},
|
||||
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
|
||||
"Emotes": { "ID": 0, "AnimID": 2 },
|
||||
"EmotesText": {
|
||||
"ID": 0, "Command": 1, "EmoteRef": 2,
|
||||
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
|
||||
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
|
||||
"ID": 0,
|
||||
"Command": 1,
|
||||
"EmoteRef": 2,
|
||||
"OthersTargetTextID": 3,
|
||||
"SenderTargetTextID": 5,
|
||||
"OthersNoTargetTextID": 7,
|
||||
"SenderNoTargetTextID": 9
|
||||
},
|
||||
"EmotesTextData": {
|
||||
"ID": 0,
|
||||
"Text": 1
|
||||
},
|
||||
"EmotesTextData": { "ID": 0, "Text": 1 },
|
||||
"Light": {
|
||||
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
|
||||
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
|
||||
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"X": 2,
|
||||
"Z": 3,
|
||||
"Y": 4,
|
||||
"InnerRadius": 5,
|
||||
"OuterRadius": 6,
|
||||
"LightParamsID": 7,
|
||||
"LightParamsIDRain": 8,
|
||||
"LightParamsIDUnderwater": 9
|
||||
},
|
||||
"LightParams": {
|
||||
"LightParamsID": 0
|
||||
},
|
||||
"LightParams": { "LightParamsID": 0 },
|
||||
"LightIntBand": {
|
||||
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||
"BlockIndex": 1,
|
||||
"NumKeyframes": 2,
|
||||
"TimeKey0": 3,
|
||||
"Value0": 19
|
||||
},
|
||||
"LightFloatBand": {
|
||||
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||
"BlockIndex": 1,
|
||||
"NumKeyframes": 2,
|
||||
"TimeKey0": 3,
|
||||
"Value0": 19
|
||||
},
|
||||
"WorldMapArea": {
|
||||
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
|
||||
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
|
||||
"DisplayMapID": 8, "ParentWorldMapID": 10
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"AreaID": 2,
|
||||
"AreaName": 3,
|
||||
"LocLeft": 4,
|
||||
"LocRight": 5,
|
||||
"LocTop": 6,
|
||||
"LocBottom": 7,
|
||||
"DisplayMapID": 8,
|
||||
"ParentWorldMapID": 10
|
||||
},
|
||||
"SpellItemEnchantment": {
|
||||
"ID": 0, "Name": 8
|
||||
"ID": 0,
|
||||
"Name": 8
|
||||
},
|
||||
"ItemSet": {
|
||||
"ID": 0, "Name": 1,
|
||||
"Item0": 18, "Item1": 19, "Item2": 20, "Item3": 21, "Item4": 22,
|
||||
"Item5": 23, "Item6": 24, "Item7": 25, "Item8": 26, "Item9": 27,
|
||||
"Spell0": 28, "Spell1": 29, "Spell2": 30, "Spell3": 31, "Spell4": 32,
|
||||
"Spell5": 33, "Spell6": 34, "Spell7": 35, "Spell8": 36, "Spell9": 37,
|
||||
"Threshold0": 38, "Threshold1": 39, "Threshold2": 40, "Threshold3": 41,
|
||||
"Threshold4": 42, "Threshold5": 43, "Threshold6": 44, "Threshold7": 45,
|
||||
"Threshold8": 46, "Threshold9": 47
|
||||
"ID": 0,
|
||||
"Name": 1,
|
||||
"Item0": 18,
|
||||
"Item1": 19,
|
||||
"Item2": 20,
|
||||
"Item3": 21,
|
||||
"Item4": 22,
|
||||
"Item5": 23,
|
||||
"Item6": 24,
|
||||
"Item7": 25,
|
||||
"Item8": 26,
|
||||
"Item9": 27,
|
||||
"Spell0": 28,
|
||||
"Spell1": 29,
|
||||
"Spell2": 30,
|
||||
"Spell3": 31,
|
||||
"Spell4": 32,
|
||||
"Spell5": 33,
|
||||
"Spell6": 34,
|
||||
"Spell7": 35,
|
||||
"Spell8": 36,
|
||||
"Spell9": 37,
|
||||
"Threshold0": 38,
|
||||
"Threshold1": 39,
|
||||
"Threshold2": 40,
|
||||
"Threshold3": 41,
|
||||
"Threshold4": 42,
|
||||
"Threshold5": 43,
|
||||
"Threshold6": 44,
|
||||
"Threshold7": 45,
|
||||
"Threshold8": 46,
|
||||
"Threshold9": 47
|
||||
},
|
||||
"SpellVisual": {
|
||||
"ID": 0, "CastKit": 2, "ImpactKit": 3, "MissileModel": 8
|
||||
"ID": 0,
|
||||
"CastKit": 2,
|
||||
"ImpactKit": 3,
|
||||
"MissileModel": 8
|
||||
},
|
||||
"SpellVisualKit": {
|
||||
"ID": 0, "BaseEffect": 5, "SpecialEffect0": 11, "SpecialEffect1": 12, "SpecialEffect2": 13
|
||||
"ID": 0,
|
||||
"BaseEffect": 5,
|
||||
"SpecialEffect0": 11,
|
||||
"SpecialEffect1": 12,
|
||||
"SpecialEffect2": 13
|
||||
},
|
||||
"SpellVisualEffectName": {
|
||||
"ID": 0, "FilePath": 2
|
||||
"ID": 0,
|
||||
"FilePath": 2
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,121 +1,293 @@
|
|||
{
|
||||
"Spell": {
|
||||
"ID": 0, "Attributes": 5, "AttributesEx": 6, "IconID": 117,
|
||||
"Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1,
|
||||
"CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33,
|
||||
"ID": 0,
|
||||
"Attributes": 5,
|
||||
"AttributesEx": 6,
|
||||
"IconID": 117,
|
||||
"Name": 120,
|
||||
"Tooltip": 147,
|
||||
"Rank": 129,
|
||||
"SchoolEnum": 1,
|
||||
"CastingTimeIndex": 15,
|
||||
"PowerType": 28,
|
||||
"ManaCost": 29,
|
||||
"RangeIndex": 33,
|
||||
"DispelType": 4
|
||||
},
|
||||
"SpellRange": { "MaxRange": 2 },
|
||||
"SpellRange": {
|
||||
"MaxRange": 2
|
||||
},
|
||||
"ItemDisplayInfo": {
|
||||
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||
"ID": 0,
|
||||
"LeftModel": 1,
|
||||
"LeftModelTexture": 3,
|
||||
"InventoryIcon": 5,
|
||||
"GeosetGroup1": 7,
|
||||
"GeosetGroup3": 9,
|
||||
"TextureArmUpper": 14,
|
||||
"TextureArmLower": 15,
|
||||
"TextureHand": 16,
|
||||
"TextureTorsoUpper": 17,
|
||||
"TextureTorsoLower": 18,
|
||||
"TextureLegUpper": 19,
|
||||
"TextureLegLower": 20,
|
||||
"TextureFoot": 21
|
||||
},
|
||||
"CharSections": {
|
||||
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||
"VariationIndex": 4, "ColorIndex": 5,
|
||||
"Texture1": 6, "Texture2": 7, "Texture3": 8,
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"BaseSection": 3,
|
||||
"VariationIndex": 4,
|
||||
"ColorIndex": 5,
|
||||
"Texture1": 6,
|
||||
"Texture2": 7,
|
||||
"Texture3": 8,
|
||||
"Flags": 9
|
||||
},
|
||||
"SpellIcon": { "ID": 0, "Path": 1 },
|
||||
"SpellIcon": {
|
||||
"ID": 0,
|
||||
"Path": 1
|
||||
},
|
||||
"FactionTemplate": {
|
||||
"ID": 0, "Faction": 1, "FactionGroup": 3,
|
||||
"FriendGroup": 4, "EnemyGroup": 5,
|
||||
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
|
||||
"ID": 0,
|
||||
"Faction": 1,
|
||||
"FactionGroup": 3,
|
||||
"FriendGroup": 4,
|
||||
"EnemyGroup": 5,
|
||||
"Enemy0": 6,
|
||||
"Enemy1": 7,
|
||||
"Enemy2": 8,
|
||||
"Enemy3": 9
|
||||
},
|
||||
"Faction": {
|
||||
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
|
||||
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
|
||||
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||
"ReputationBase2": 12, "ReputationBase3": 13
|
||||
"ID": 0,
|
||||
"ReputationRaceMask0": 2,
|
||||
"ReputationRaceMask1": 3,
|
||||
"ReputationRaceMask2": 4,
|
||||
"ReputationRaceMask3": 5,
|
||||
"ReputationBase0": 10,
|
||||
"ReputationBase1": 11,
|
||||
"ReputationBase2": 12,
|
||||
"ReputationBase3": 13
|
||||
},
|
||||
"AreaTable": {
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"ParentAreaNum": 2,
|
||||
"ExploreFlag": 3
|
||||
},
|
||||
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||
"CreatureDisplayInfoExtra": {
|
||||
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
|
||||
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
|
||||
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
|
||||
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
|
||||
"EquipDisplay9": 17, "BakeName": 18
|
||||
"ID": 0,
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"SkinID": 3,
|
||||
"FaceID": 4,
|
||||
"HairStyleID": 5,
|
||||
"HairColorID": 6,
|
||||
"FacialHairID": 7,
|
||||
"EquipDisplay0": 8,
|
||||
"EquipDisplay1": 9,
|
||||
"EquipDisplay2": 10,
|
||||
"EquipDisplay3": 11,
|
||||
"EquipDisplay4": 12,
|
||||
"EquipDisplay5": 13,
|
||||
"EquipDisplay6": 14,
|
||||
"EquipDisplay7": 15,
|
||||
"EquipDisplay8": 16,
|
||||
"EquipDisplay9": 17,
|
||||
"BakeName": 18
|
||||
},
|
||||
"CreatureDisplayInfo": {
|
||||
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
|
||||
"Skin1": 6, "Skin2": 7, "Skin3": 8
|
||||
"ID": 0,
|
||||
"ModelID": 1,
|
||||
"ExtraDisplayId": 3,
|
||||
"Skin1": 6,
|
||||
"Skin2": 7,
|
||||
"Skin3": 8
|
||||
},
|
||||
"TaxiNodes": {
|
||||
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"X": 2,
|
||||
"Y": 3,
|
||||
"Z": 4,
|
||||
"Name": 5
|
||||
},
|
||||
"TaxiPath": {
|
||||
"ID": 0,
|
||||
"FromNode": 1,
|
||||
"ToNode": 2,
|
||||
"Cost": 3
|
||||
},
|
||||
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
|
||||
"TaxiPathNode": {
|
||||
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
|
||||
"X": 4, "Y": 5, "Z": 6
|
||||
"ID": 0,
|
||||
"PathID": 1,
|
||||
"NodeIndex": 2,
|
||||
"MapID": 3,
|
||||
"X": 4,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
},
|
||||
"TalentTab": {
|
||||
"ID": 0, "Name": 1, "ClassMask": 12,
|
||||
"OrderIndex": 14, "BackgroundFile": 15
|
||||
"ID": 0,
|
||||
"Name": 1,
|
||||
"ClassMask": 12,
|
||||
"OrderIndex": 14,
|
||||
"BackgroundFile": 15
|
||||
},
|
||||
"Talent": {
|
||||
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
|
||||
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
|
||||
"ID": 0,
|
||||
"TabID": 1,
|
||||
"Row": 2,
|
||||
"Column": 3,
|
||||
"RankSpell0": 4,
|
||||
"PrereqTalent0": 9,
|
||||
"PrereqRank0": 12
|
||||
},
|
||||
"SkillLineAbility": {
|
||||
"SkillLineID": 1,
|
||||
"SpellID": 2
|
||||
},
|
||||
"SkillLine": {
|
||||
"ID": 0,
|
||||
"Category": 1,
|
||||
"Name": 3
|
||||
},
|
||||
"Map": {
|
||||
"ID": 0,
|
||||
"InternalName": 1
|
||||
},
|
||||
"CreatureModelData": {
|
||||
"ID": 0,
|
||||
"ModelPath": 2
|
||||
},
|
||||
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
|
||||
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
|
||||
"Map": { "ID": 0, "InternalName": 1 },
|
||||
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
|
||||
"CharHairGeosets": {
|
||||
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"Variation": 3,
|
||||
"GeosetID": 4
|
||||
},
|
||||
"CharacterFacialHairStyles": {
|
||||
"RaceID": 0, "SexID": 1, "Variation": 2,
|
||||
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
|
||||
"RaceID": 0,
|
||||
"SexID": 1,
|
||||
"Variation": 2,
|
||||
"Geoset100": 3,
|
||||
"Geoset300": 4,
|
||||
"Geoset200": 5
|
||||
},
|
||||
"GameObjectDisplayInfo": {
|
||||
"ID": 0,
|
||||
"ModelName": 1
|
||||
},
|
||||
"Emotes": {
|
||||
"ID": 0,
|
||||
"AnimID": 2
|
||||
},
|
||||
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
|
||||
"Emotes": { "ID": 0, "AnimID": 2 },
|
||||
"EmotesText": {
|
||||
"ID": 0, "Command": 1, "EmoteRef": 2,
|
||||
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
|
||||
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
|
||||
"ID": 0,
|
||||
"Command": 1,
|
||||
"EmoteRef": 2,
|
||||
"OthersTargetTextID": 3,
|
||||
"SenderTargetTextID": 5,
|
||||
"OthersNoTargetTextID": 7,
|
||||
"SenderNoTargetTextID": 9
|
||||
},
|
||||
"EmotesTextData": {
|
||||
"ID": 0,
|
||||
"Text": 1
|
||||
},
|
||||
"EmotesTextData": { "ID": 0, "Text": 1 },
|
||||
"Light": {
|
||||
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
|
||||
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
|
||||
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"X": 2,
|
||||
"Z": 3,
|
||||
"Y": 4,
|
||||
"InnerRadius": 5,
|
||||
"OuterRadius": 6,
|
||||
"LightParamsID": 7,
|
||||
"LightParamsIDRain": 8,
|
||||
"LightParamsIDUnderwater": 9
|
||||
},
|
||||
"LightParams": {
|
||||
"LightParamsID": 0
|
||||
},
|
||||
"LightParams": { "LightParamsID": 0 },
|
||||
"LightIntBand": {
|
||||
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||
"BlockIndex": 1,
|
||||
"NumKeyframes": 2,
|
||||
"TimeKey0": 3,
|
||||
"Value0": 19
|
||||
},
|
||||
"LightFloatBand": {
|
||||
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||
"BlockIndex": 1,
|
||||
"NumKeyframes": 2,
|
||||
"TimeKey0": 3,
|
||||
"Value0": 19
|
||||
},
|
||||
"WorldMapArea": {
|
||||
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
|
||||
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
|
||||
"DisplayMapID": 8, "ParentWorldMapID": 10
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"AreaID": 2,
|
||||
"AreaName": 3,
|
||||
"LocLeft": 4,
|
||||
"LocRight": 5,
|
||||
"LocTop": 6,
|
||||
"LocBottom": 7,
|
||||
"DisplayMapID": 8,
|
||||
"ParentWorldMapID": 10
|
||||
},
|
||||
"SpellItemEnchantment": {
|
||||
"ID": 0, "Name": 8
|
||||
"ID": 0,
|
||||
"Name": 8
|
||||
},
|
||||
"ItemSet": {
|
||||
"ID": 0, "Name": 1,
|
||||
"Item0": 10, "Item1": 11, "Item2": 12, "Item3": 13, "Item4": 14,
|
||||
"Item5": 15, "Item6": 16, "Item7": 17, "Item8": 18, "Item9": 19,
|
||||
"Spell0": 20, "Spell1": 21, "Spell2": 22, "Spell3": 23, "Spell4": 24,
|
||||
"Spell5": 25, "Spell6": 26, "Spell7": 27, "Spell8": 28, "Spell9": 29,
|
||||
"Threshold0": 30, "Threshold1": 31, "Threshold2": 32, "Threshold3": 33,
|
||||
"Threshold4": 34, "Threshold5": 35, "Threshold6": 36, "Threshold7": 37,
|
||||
"Threshold8": 38, "Threshold9": 39
|
||||
"ID": 0,
|
||||
"Name": 1,
|
||||
"Item0": 10,
|
||||
"Item1": 11,
|
||||
"Item2": 12,
|
||||
"Item3": 13,
|
||||
"Item4": 14,
|
||||
"Item5": 15,
|
||||
"Item6": 16,
|
||||
"Item7": 17,
|
||||
"Item8": 18,
|
||||
"Item9": 19,
|
||||
"Spell0": 20,
|
||||
"Spell1": 21,
|
||||
"Spell2": 22,
|
||||
"Spell3": 23,
|
||||
"Spell4": 24,
|
||||
"Spell5": 25,
|
||||
"Spell6": 26,
|
||||
"Spell7": 27,
|
||||
"Spell8": 28,
|
||||
"Spell9": 29,
|
||||
"Threshold0": 30,
|
||||
"Threshold1": 31,
|
||||
"Threshold2": 32,
|
||||
"Threshold3": 33,
|
||||
"Threshold4": 34,
|
||||
"Threshold5": 35,
|
||||
"Threshold6": 36,
|
||||
"Threshold7": 37,
|
||||
"Threshold8": 38,
|
||||
"Threshold9": 39
|
||||
},
|
||||
"SpellVisual": {
|
||||
"ID": 0, "CastKit": 2, "ImpactKit": 3, "MissileModel": 8
|
||||
"ID": 0,
|
||||
"CastKit": 2,
|
||||
"ImpactKit": 3,
|
||||
"MissileModel": 8
|
||||
},
|
||||
"SpellVisualKit": {
|
||||
"ID": 0, "BaseEffect": 5, "SpecialEffect0": 11, "SpecialEffect1": 12, "SpecialEffect2": 13
|
||||
"ID": 0,
|
||||
"BaseEffect": 5,
|
||||
"SpecialEffect0": 11,
|
||||
"SpecialEffect1": 12,
|
||||
"SpecialEffect2": 13
|
||||
},
|
||||
"SpellVisualEffectName": {
|
||||
"ID": 0, "FilePath": 2
|
||||
"ID": 0,
|
||||
"FilePath": 2
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,129 +1,319 @@
|
|||
{
|
||||
"Spell": {
|
||||
"ID": 0, "Attributes": 4, "AttributesEx": 5, "IconID": 133,
|
||||
"Name": 136, "Tooltip": 139, "Rank": 153, "SchoolMask": 225,
|
||||
"PowerType": 14, "ManaCost": 39, "CastingTimeIndex": 47, "RangeIndex": 49,
|
||||
"ID": 0,
|
||||
"Attributes": 4,
|
||||
"AttributesEx": 5,
|
||||
"IconID": 133,
|
||||
"Name": 136,
|
||||
"Tooltip": 139,
|
||||
"Rank": 153,
|
||||
"SchoolMask": 225,
|
||||
"PowerType": 14,
|
||||
"ManaCost": 39,
|
||||
"CastingTimeIndex": 47,
|
||||
"RangeIndex": 49,
|
||||
"DispelType": 2
|
||||
},
|
||||
"SpellRange": { "MaxRange": 4 },
|
||||
"SpellRange": {
|
||||
"MaxRange": 4
|
||||
},
|
||||
"ItemDisplayInfo": {
|
||||
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||
"ID": 0,
|
||||
"LeftModel": 1,
|
||||
"LeftModelTexture": 3,
|
||||
"InventoryIcon": 5,
|
||||
"GeosetGroup1": 7,
|
||||
"GeosetGroup3": 9,
|
||||
"TextureArmUpper": 14,
|
||||
"TextureArmLower": 15,
|
||||
"TextureHand": 16,
|
||||
"TextureTorsoUpper": 17,
|
||||
"TextureTorsoLower": 18,
|
||||
"TextureLegUpper": 19,
|
||||
"TextureLegLower": 20,
|
||||
"TextureFoot": 21
|
||||
},
|
||||
"CharSections": {
|
||||
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||
"VariationIndex": 4, "ColorIndex": 5,
|
||||
"Texture1": 6, "Texture2": 7, "Texture3": 8,
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"BaseSection": 3,
|
||||
"VariationIndex": 4,
|
||||
"ColorIndex": 5,
|
||||
"Texture1": 6,
|
||||
"Texture2": 7,
|
||||
"Texture3": 8,
|
||||
"Flags": 9
|
||||
},
|
||||
"SpellIcon": { "ID": 0, "Path": 1 },
|
||||
"SpellIcon": {
|
||||
"ID": 0,
|
||||
"Path": 1
|
||||
},
|
||||
"FactionTemplate": {
|
||||
"ID": 0, "Faction": 1, "FactionGroup": 3,
|
||||
"FriendGroup": 4, "EnemyGroup": 5,
|
||||
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
|
||||
"ID": 0,
|
||||
"Faction": 1,
|
||||
"FactionGroup": 3,
|
||||
"FriendGroup": 4,
|
||||
"EnemyGroup": 5,
|
||||
"Enemy0": 6,
|
||||
"Enemy1": 7,
|
||||
"Enemy2": 8,
|
||||
"Enemy3": 9
|
||||
},
|
||||
"Faction": {
|
||||
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
|
||||
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
|
||||
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||
"ReputationBase2": 12, "ReputationBase3": 13
|
||||
"ID": 0,
|
||||
"ReputationRaceMask0": 2,
|
||||
"ReputationRaceMask1": 3,
|
||||
"ReputationRaceMask2": 4,
|
||||
"ReputationRaceMask3": 5,
|
||||
"ReputationBase0": 10,
|
||||
"ReputationBase1": 11,
|
||||
"ReputationBase2": 12,
|
||||
"ReputationBase3": 13
|
||||
},
|
||||
"CharTitles": {
|
||||
"ID": 0,
|
||||
"Title": 2,
|
||||
"TitleBit": 36
|
||||
},
|
||||
"Achievement": {
|
||||
"ID": 0,
|
||||
"Title": 4,
|
||||
"Description": 21,
|
||||
"Points": 39
|
||||
},
|
||||
"AchievementCriteria": {
|
||||
"ID": 0,
|
||||
"AchievementID": 1,
|
||||
"Quantity": 4,
|
||||
"Description": 9
|
||||
},
|
||||
"AreaTable": {
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"ParentAreaNum": 2,
|
||||
"ExploreFlag": 3
|
||||
},
|
||||
"CharTitles": { "ID": 0, "Title": 2, "TitleBit": 36 },
|
||||
"Achievement": { "ID": 0, "Title": 4, "Description": 21, "Points": 39 },
|
||||
"AchievementCriteria": { "ID": 0, "AchievementID": 1, "Quantity": 4, "Description": 9 },
|
||||
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||
"CreatureDisplayInfoExtra": {
|
||||
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
|
||||
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
|
||||
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
|
||||
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
|
||||
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
|
||||
"ID": 0,
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"SkinID": 3,
|
||||
"FaceID": 4,
|
||||
"HairStyleID": 5,
|
||||
"HairColorID": 6,
|
||||
"FacialHairID": 7,
|
||||
"EquipDisplay0": 8,
|
||||
"EquipDisplay1": 9,
|
||||
"EquipDisplay2": 10,
|
||||
"EquipDisplay3": 11,
|
||||
"EquipDisplay4": 12,
|
||||
"EquipDisplay5": 13,
|
||||
"EquipDisplay6": 14,
|
||||
"EquipDisplay7": 15,
|
||||
"EquipDisplay8": 16,
|
||||
"EquipDisplay9": 17,
|
||||
"EquipDisplay10": 18,
|
||||
"BakeName": 20
|
||||
},
|
||||
"CreatureDisplayInfo": {
|
||||
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
|
||||
"Skin1": 6, "Skin2": 7, "Skin3": 8
|
||||
"ID": 0,
|
||||
"ModelID": 1,
|
||||
"ExtraDisplayId": 3,
|
||||
"Skin1": 6,
|
||||
"Skin2": 7,
|
||||
"Skin3": 8
|
||||
},
|
||||
"TaxiNodes": {
|
||||
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5,
|
||||
"MountDisplayIdAllianceFallback": 20, "MountDisplayIdHordeFallback": 21,
|
||||
"MountDisplayIdAlliance": 22, "MountDisplayIdHorde": 23
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"X": 2,
|
||||
"Y": 3,
|
||||
"Z": 4,
|
||||
"Name": 5,
|
||||
"MountDisplayIdAllianceFallback": 20,
|
||||
"MountDisplayIdHordeFallback": 21,
|
||||
"MountDisplayIdAlliance": 22,
|
||||
"MountDisplayIdHorde": 23
|
||||
},
|
||||
"TaxiPath": {
|
||||
"ID": 0,
|
||||
"FromNode": 1,
|
||||
"ToNode": 2,
|
||||
"Cost": 3
|
||||
},
|
||||
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
|
||||
"TaxiPathNode": {
|
||||
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
|
||||
"X": 4, "Y": 5, "Z": 6
|
||||
"ID": 0,
|
||||
"PathID": 1,
|
||||
"NodeIndex": 2,
|
||||
"MapID": 3,
|
||||
"X": 4,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
},
|
||||
"TalentTab": {
|
||||
"ID": 0, "Name": 1, "ClassMask": 20,
|
||||
"OrderIndex": 22, "BackgroundFile": 23
|
||||
"ID": 0,
|
||||
"Name": 1,
|
||||
"ClassMask": 20,
|
||||
"OrderIndex": 22,
|
||||
"BackgroundFile": 23
|
||||
},
|
||||
"Talent": {
|
||||
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
|
||||
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
|
||||
"ID": 0,
|
||||
"TabID": 1,
|
||||
"Row": 2,
|
||||
"Column": 3,
|
||||
"RankSpell0": 4,
|
||||
"PrereqTalent0": 9,
|
||||
"PrereqRank0": 12
|
||||
},
|
||||
"SkillLineAbility": {
|
||||
"SkillLineID": 1,
|
||||
"SpellID": 2
|
||||
},
|
||||
"SkillLine": {
|
||||
"ID": 0,
|
||||
"Category": 1,
|
||||
"Name": 3
|
||||
},
|
||||
"Map": {
|
||||
"ID": 0,
|
||||
"InternalName": 1
|
||||
},
|
||||
"CreatureModelData": {
|
||||
"ID": 0,
|
||||
"ModelPath": 2
|
||||
},
|
||||
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
|
||||
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
|
||||
"Map": { "ID": 0, "InternalName": 1 },
|
||||
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
|
||||
"CharHairGeosets": {
|
||||
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
|
||||
"RaceID": 1,
|
||||
"SexID": 2,
|
||||
"Variation": 3,
|
||||
"GeosetID": 4
|
||||
},
|
||||
"CharacterFacialHairStyles": {
|
||||
"RaceID": 0, "SexID": 1, "Variation": 2,
|
||||
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
|
||||
"RaceID": 0,
|
||||
"SexID": 1,
|
||||
"Variation": 2,
|
||||
"Geoset100": 3,
|
||||
"Geoset300": 4,
|
||||
"Geoset200": 5
|
||||
},
|
||||
"GameObjectDisplayInfo": {
|
||||
"ID": 0,
|
||||
"ModelName": 1
|
||||
},
|
||||
"Emotes": {
|
||||
"ID": 0,
|
||||
"AnimID": 2
|
||||
},
|
||||
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
|
||||
"Emotes": { "ID": 0, "AnimID": 2 },
|
||||
"EmotesText": {
|
||||
"ID": 0, "Command": 1, "EmoteRef": 2,
|
||||
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
|
||||
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
|
||||
"ID": 0,
|
||||
"Command": 1,
|
||||
"EmoteRef": 2,
|
||||
"OthersTargetTextID": 3,
|
||||
"SenderTargetTextID": 5,
|
||||
"OthersNoTargetTextID": 7,
|
||||
"SenderNoTargetTextID": 9
|
||||
},
|
||||
"EmotesTextData": {
|
||||
"ID": 0,
|
||||
"Text": 1
|
||||
},
|
||||
"EmotesTextData": { "ID": 0, "Text": 1 },
|
||||
"Light": {
|
||||
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
|
||||
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
|
||||
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"X": 2,
|
||||
"Z": 3,
|
||||
"Y": 4,
|
||||
"InnerRadius": 5,
|
||||
"OuterRadius": 6,
|
||||
"LightParamsID": 7,
|
||||
"LightParamsIDRain": 8,
|
||||
"LightParamsIDUnderwater": 9
|
||||
},
|
||||
"LightParams": {
|
||||
"LightParamsID": 0
|
||||
},
|
||||
"LightParams": { "LightParamsID": 0 },
|
||||
"LightIntBand": {
|
||||
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||
"BlockIndex": 1,
|
||||
"NumKeyframes": 2,
|
||||
"TimeKey0": 3,
|
||||
"Value0": 19
|
||||
},
|
||||
"LightFloatBand": {
|
||||
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||
"BlockIndex": 1,
|
||||
"NumKeyframes": 2,
|
||||
"TimeKey0": 3,
|
||||
"Value0": 19
|
||||
},
|
||||
"WorldMapArea": {
|
||||
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
|
||||
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
|
||||
"DisplayMapID": 8, "ParentWorldMapID": 10
|
||||
"ID": 0,
|
||||
"MapID": 1,
|
||||
"AreaID": 2,
|
||||
"AreaName": 3,
|
||||
"LocLeft": 4,
|
||||
"LocRight": 5,
|
||||
"LocTop": 6,
|
||||
"LocBottom": 7,
|
||||
"DisplayMapID": 8,
|
||||
"ParentWorldMapID": 10
|
||||
},
|
||||
"SpellItemEnchantment": {
|
||||
"ID": 0, "Name": 8
|
||||
"ID": 0,
|
||||
"Name": 8
|
||||
},
|
||||
"ItemSet": {
|
||||
"ID": 0, "Name": 1,
|
||||
"Item0": 18, "Item1": 19, "Item2": 20, "Item3": 21, "Item4": 22,
|
||||
"Item5": 23, "Item6": 24, "Item7": 25, "Item8": 26, "Item9": 27,
|
||||
"Spell0": 28, "Spell1": 29, "Spell2": 30, "Spell3": 31, "Spell4": 32,
|
||||
"Spell5": 33, "Spell6": 34, "Spell7": 35, "Spell8": 36, "Spell9": 37,
|
||||
"Threshold0": 38, "Threshold1": 39, "Threshold2": 40, "Threshold3": 41,
|
||||
"Threshold4": 42, "Threshold5": 43, "Threshold6": 44, "Threshold7": 45,
|
||||
"Threshold8": 46, "Threshold9": 47
|
||||
"ID": 0,
|
||||
"Name": 1,
|
||||
"Item0": 18,
|
||||
"Item1": 19,
|
||||
"Item2": 20,
|
||||
"Item3": 21,
|
||||
"Item4": 22,
|
||||
"Item5": 23,
|
||||
"Item6": 24,
|
||||
"Item7": 25,
|
||||
"Item8": 26,
|
||||
"Item9": 27,
|
||||
"Spell0": 28,
|
||||
"Spell1": 29,
|
||||
"Spell2": 30,
|
||||
"Spell3": 31,
|
||||
"Spell4": 32,
|
||||
"Spell5": 33,
|
||||
"Spell6": 34,
|
||||
"Spell7": 35,
|
||||
"Spell8": 36,
|
||||
"Spell9": 37,
|
||||
"Threshold0": 38,
|
||||
"Threshold1": 39,
|
||||
"Threshold2": 40,
|
||||
"Threshold3": 41,
|
||||
"Threshold4": 42,
|
||||
"Threshold5": 43,
|
||||
"Threshold6": 44,
|
||||
"Threshold7": 45,
|
||||
"Threshold8": 46,
|
||||
"Threshold9": 47
|
||||
},
|
||||
"LFGDungeons": {
|
||||
"ID": 0, "Name": 1
|
||||
"ID": 0,
|
||||
"Name": 1
|
||||
},
|
||||
"SpellVisual": {
|
||||
"ID": 0, "CastKit": 2, "ImpactKit": 3, "MissileModel": 8
|
||||
"ID": 0,
|
||||
"CastKit": 2,
|
||||
"ImpactKit": 3,
|
||||
"MissileModel": 8
|
||||
},
|
||||
"SpellVisualKit": {
|
||||
"ID": 0, "BaseEffect": 5, "SpecialEffect0": 11, "SpecialEffect1": 12, "SpecialEffect2": 13
|
||||
"ID": 0,
|
||||
"BaseEffect": 5,
|
||||
"SpecialEffect0": 11,
|
||||
"SpecialEffect1": 12,
|
||||
"SpecialEffect2": 13
|
||||
},
|
||||
"SpellVisualEffectName": {
|
||||
"ID": 0, "FilePath": 2
|
||||
"ID": 0,
|
||||
"FilePath": 2
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -830,6 +830,14 @@ public:
|
|||
void togglePetSpellAutocast(uint32_t spellId);
|
||||
const std::unordered_set<uint32_t>& getKnownSpells() const { return knownSpells; }
|
||||
|
||||
// Spell book tabs — groups known spells by class skill line for Lua API
|
||||
struct SpellBookTab {
|
||||
std::string name;
|
||||
std::string texture; // icon path
|
||||
std::vector<uint32_t> spellIds; // spells in this tab
|
||||
};
|
||||
const std::vector<SpellBookTab>& getSpellBookTabs();
|
||||
|
||||
// ---- Pet Stable ----
|
||||
struct StabledPet {
|
||||
uint32_t petNumber = 0; // server-side pet number (used for unstable/swap)
|
||||
|
|
@ -882,6 +890,7 @@ public:
|
|||
uint32_t getCurrentCastSpellId() const { return currentCastSpellId; }
|
||||
float getCastProgress() const { return castTimeTotal > 0 ? (castTimeTotal - castTimeRemaining) / castTimeTotal : 0.0f; }
|
||||
float getCastTimeRemaining() const { return castTimeRemaining; }
|
||||
float getCastTimeTotal() const { return castTimeTotal; }
|
||||
|
||||
// Repeat-craft queue
|
||||
void startCraftQueue(uint32_t spellId, int count);
|
||||
|
|
@ -896,6 +905,7 @@ public:
|
|||
// Unit cast state (tracked per GUID for target frame + boss frames)
|
||||
struct UnitCastState {
|
||||
bool casting = false;
|
||||
bool isChannel = false; ///< true for channels (MSG_CHANNEL_START), false for casts (SMSG_SPELL_START)
|
||||
uint32_t spellId = 0;
|
||||
float timeRemaining = 0.0f;
|
||||
float timeTotal = 0.0f;
|
||||
|
|
@ -1672,6 +1682,8 @@ public:
|
|||
std::array<QuestRewardItem, 6> rewardChoiceItems{}; // player picks one of these
|
||||
};
|
||||
const std::vector<QuestLogEntry>& getQuestLog() const { return questLog_; }
|
||||
int getSelectedQuestLogIndex() const { return selectedQuestLogIndex_; }
|
||||
void setSelectedQuestLogIndex(int idx) { selectedQuestLogIndex_ = idx; }
|
||||
void abandonQuest(uint32_t questId);
|
||||
void shareQuestWithParty(uint32_t questId); // CMSG_PUSHQUESTTOPARTY
|
||||
bool requestQuestQuery(uint32_t questId, bool force = false);
|
||||
|
|
@ -3185,6 +3197,7 @@ private:
|
|||
|
||||
// Quest log
|
||||
std::vector<QuestLogEntry> questLog_;
|
||||
int selectedQuestLogIndex_ = 0;
|
||||
std::unordered_set<uint32_t> pendingQuestQueryIds_;
|
||||
std::unordered_set<uint32_t> trackedQuestIds_;
|
||||
bool pendingLoginQuestResync_ = false;
|
||||
|
|
@ -3438,6 +3451,8 @@ private:
|
|||
std::unordered_map<uint32_t, std::string> skillLineNames_;
|
||||
std::unordered_map<uint32_t, uint32_t> skillLineCategories_;
|
||||
std::unordered_map<uint32_t, uint32_t> spellToSkillLine_; // spellID -> skillLineID
|
||||
std::vector<SpellBookTab> spellBookTabs_;
|
||||
bool spellBookTabsDirty_ = true;
|
||||
bool skillLineDbcLoaded_ = false;
|
||||
bool skillLineAbilityLoaded_ = false;
|
||||
static constexpr size_t PLAYER_EXPLORED_ZONES_COUNT = 128;
|
||||
|
|
|
|||
|
|
@ -399,9 +399,10 @@ enum class MovementFlags : uint32_t {
|
|||
WATER_WALK = 0x00008000, // Walk on water surface
|
||||
SWIMMING = 0x00200000,
|
||||
ASCENDING = 0x00400000,
|
||||
CAN_FLY = 0x00800000,
|
||||
FLYING = 0x01000000,
|
||||
HOVER = 0x02000000,
|
||||
DESCENDING = 0x00800000,
|
||||
CAN_FLY = 0x01000000,
|
||||
FLYING = 0x02000000,
|
||||
HOVER = 0x40000000,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -416,6 +416,13 @@ private:
|
|||
static constexpr uint32_t MAX_MATERIAL_SETS = 8192;
|
||||
static constexpr uint32_t MAX_BONE_SETS = 8192;
|
||||
|
||||
// Dummy identity bone buffer + descriptor set for non-animated models.
|
||||
// The pipeline layout declares set 2 (bones) and some drivers (Intel ANV)
|
||||
// require all declared sets to be bound even when the shader doesn't access them.
|
||||
::VkBuffer dummyBoneBuffer_ = VK_NULL_HANDLE;
|
||||
VmaAllocation dummyBoneAlloc_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSet dummyBoneSet_ = VK_NULL_HANDLE;
|
||||
|
||||
// Dynamic ribbon vertex buffer (CPU-written triangle strip)
|
||||
static constexpr size_t MAX_RIBBON_VERTS = 2048; // 9 floats each
|
||||
::VkBuffer ribbonVB_ = VK_NULL_HANDLE;
|
||||
|
|
|
|||
|
|
@ -394,6 +394,11 @@ private:
|
|||
std::unordered_set<uint32_t> uploadedM2Ids_;
|
||||
std::mutex uploadedM2IdsMutex_;
|
||||
|
||||
// Cross-tile dedup for WMO doodad preparation on background workers
|
||||
// (prevents re-parsing thousands of doodads when same WMO spans multiple tiles)
|
||||
std::unordered_set<uint32_t> preparedWmoUniqueIds_;
|
||||
std::mutex preparedWmoUniqueIdsMutex_;
|
||||
|
||||
// Dedup set for doodad placements across tile boundaries
|
||||
std::unordered_set<uint32_t> placedDoodadIds;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5353,9 +5353,9 @@ void Application::buildCharSectionsCache() {
|
|||
uint32_t raceF = csL ? (*csL)["RaceID"] : 1;
|
||||
uint32_t sexF = csL ? (*csL)["SexID"] : 2;
|
||||
uint32_t secF = csL ? (*csL)["BaseSection"] : 3;
|
||||
uint32_t varF = csL ? (*csL)["VariationIndex"] : 4;
|
||||
uint32_t colF = csL ? (*csL)["ColorIndex"] : 5;
|
||||
uint32_t tex1F = csL ? (*csL)["Texture1"] : 6;
|
||||
uint32_t varF = csL ? (*csL)["VariationIndex"] : 8;
|
||||
uint32_t colF = csL ? (*csL)["ColorIndex"] : 9;
|
||||
uint32_t tex1F = csL ? (*csL)["Texture1"] : 4;
|
||||
for (uint32_t r = 0; r < dbc->getRecordCount(); r++) {
|
||||
uint32_t race = dbc->getUInt32(r, raceF);
|
||||
uint32_t sex = dbc->getUInt32(r, sexF);
|
||||
|
|
@ -5962,9 +5962,9 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
|
|||
if (rId != npcRace || sId != npcSex) continue;
|
||||
|
||||
uint32_t section = csDbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3);
|
||||
uint32_t variation = csDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4);
|
||||
uint32_t color = csDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5);
|
||||
uint32_t tex1F = csL ? (*csL)["Texture1"] : 6;
|
||||
uint32_t variation = csDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8);
|
||||
uint32_t color = csDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9);
|
||||
uint32_t tex1F = csL ? (*csL)["Texture1"] : 4;
|
||||
|
||||
if (section == 0 && def.basePath.empty() && color == npcSkin) {
|
||||
def.basePath = csDbc->getString(r, tex1F);
|
||||
|
|
@ -6080,11 +6080,11 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
|
|||
if (raceId != targetRace || sexId != targetSex) continue;
|
||||
uint32_t section = csDbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3);
|
||||
if (section != 3) continue;
|
||||
uint32_t variation = csDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4);
|
||||
uint32_t colorIdx = csDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5);
|
||||
uint32_t variation = csDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8);
|
||||
uint32_t colorIdx = csDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9);
|
||||
if (variation != static_cast<uint32_t>(extraCopy.hairStyleId)) continue;
|
||||
if (colorIdx != static_cast<uint32_t>(extraCopy.hairColorId)) continue;
|
||||
def.hairTexturePath = csDbc->getString(r, csL ? (*csL)["Texture1"] : 6);
|
||||
def.hairTexturePath = csDbc->getString(r, csL ? (*csL)["Texture1"] : 4);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -7193,7 +7193,7 @@ void Application::spawnOnlinePlayer(uint64_t guid,
|
|||
const auto* csL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("CharSections") : nullptr;
|
||||
uint32_t targetRaceId = raceId;
|
||||
uint32_t targetSexId = genderId;
|
||||
const uint32_t csTex1 = csL ? (*csL)["Texture1"] : 6;
|
||||
const uint32_t csTex1 = csL ? (*csL)["Texture1"] : 4;
|
||||
|
||||
bool foundSkin = false;
|
||||
bool foundUnderwear = false;
|
||||
|
|
@ -7204,8 +7204,8 @@ void Application::spawnOnlinePlayer(uint64_t guid,
|
|||
uint32_t rRace = charSectionsDbc->getUInt32(r, csL ? (*csL)["RaceID"] : 1);
|
||||
uint32_t rSex = charSectionsDbc->getUInt32(r, csL ? (*csL)["SexID"] : 2);
|
||||
uint32_t baseSection = charSectionsDbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3);
|
||||
uint32_t variationIndex = charSectionsDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4);
|
||||
uint32_t colorIndex = charSectionsDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5);
|
||||
uint32_t variationIndex = charSectionsDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8);
|
||||
uint32_t colorIndex = charSectionsDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9);
|
||||
|
||||
if (rRace != targetRaceId || rSex != targetSexId) continue;
|
||||
|
||||
|
|
@ -8189,9 +8189,9 @@ void Application::processCreatureSpawnQueue(bool unlimited) {
|
|||
uint32_t sId = csDbc->getUInt32(r, csL ? (*csL)["SexID"] : 2);
|
||||
if (rId != nRace || sId != nSex) continue;
|
||||
uint32_t section = csDbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3);
|
||||
uint32_t variation = csDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4);
|
||||
uint32_t color = csDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5);
|
||||
uint32_t tex1F = csL ? (*csL)["Texture1"] : 6;
|
||||
uint32_t variation = csDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8);
|
||||
uint32_t color = csDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9);
|
||||
uint32_t tex1F = csL ? (*csL)["Texture1"] : 4;
|
||||
if (section == 0 && color == nSkin) {
|
||||
std::string t = csDbc->getString(r, tex1F);
|
||||
if (!t.empty()) displaySkinPaths.push_back(t);
|
||||
|
|
|
|||
|
|
@ -7519,6 +7519,7 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
} else {
|
||||
auto& s = unitCastStates_[chanCaster];
|
||||
s.casting = true;
|
||||
s.isChannel = true;
|
||||
s.spellId = chanSpellId;
|
||||
s.timeTotal = chanTotalMs / 1000.0f;
|
||||
s.timeRemaining = s.timeTotal;
|
||||
|
|
@ -9743,6 +9744,19 @@ void GameHandler::handleLoginVerifyWorld(network::Packet& packet) {
|
|||
LOG_INFO("Auto-requested played time on login");
|
||||
}
|
||||
}
|
||||
|
||||
// Fire PLAYER_ENTERING_WORLD — THE most important event for addon initialization.
|
||||
// Fires on initial login, teleports, instance transitions, and zone changes.
|
||||
if (addonEventCallback_) {
|
||||
addonEventCallback_("PLAYER_ENTERING_WORLD", {initialWorldEntry ? "1" : "0"});
|
||||
// Also fire ZONE_CHANGED_NEW_AREA and UPDATE_WORLD_STATES so map/BG addons refresh
|
||||
addonEventCallback_("ZONE_CHANGED_NEW_AREA", {});
|
||||
addonEventCallback_("UPDATE_WORLD_STATES", {});
|
||||
// PLAYER_LOGIN fires only on initial login (not teleports)
|
||||
if (initialWorldEntry) {
|
||||
addonEventCallback_("PLAYER_LOGIN", {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameHandler::handleClientCacheVersion(network::Packet& packet) {
|
||||
|
|
@ -12025,7 +12039,7 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem
|
|||
}
|
||||
}
|
||||
unit->setMountDisplayId(val);
|
||||
} else if (key == ufNpcFlags) { unit->setNpcFlags(val); }
|
||||
}
|
||||
}
|
||||
if (block.guid == playerGuid) {
|
||||
constexpr uint32_t UNIT_FLAG_TAXI_FLIGHT = 0x00000100;
|
||||
|
|
@ -12573,6 +12587,8 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem
|
|||
uint32_t old = currentMountDisplayId_;
|
||||
currentMountDisplayId_ = val;
|
||||
if (val != old && mountCallback_) mountCallback_(val);
|
||||
if (val != old && addonEventCallback_)
|
||||
addonEventCallback_("UNIT_MODEL_CHANGED", {"player"});
|
||||
if (old == 0 && val != 0) {
|
||||
mountAuraSpellId_ = 0;
|
||||
for (const auto& a : playerAuras) {
|
||||
|
|
@ -12718,6 +12734,15 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem
|
|||
qsPkt.writeUInt64(block.guid);
|
||||
socket->send(qsPkt);
|
||||
}
|
||||
// Fire UNIT_MODEL_CHANGED for addons that track model swaps
|
||||
if (addonEventCallback_) {
|
||||
std::string uid;
|
||||
if (block.guid == targetGuid) uid = "target";
|
||||
else if (block.guid == focusGuid) uid = "focus";
|
||||
else if (block.guid == petGuid_) uid = "pet";
|
||||
if (!uid.empty())
|
||||
addonEventCallback_("UNIT_MODEL_CHANGED", {uid});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update XP / inventory slot / skill fields for player entity
|
||||
|
|
@ -19317,6 +19342,12 @@ void GameHandler::handleInitialSpells(network::Packet& packet) {
|
|||
loadSkillLineAbilityDbc();
|
||||
|
||||
LOG_INFO("Learned ", knownSpells.size(), " spells");
|
||||
|
||||
// Notify addons that the full spell list is now available
|
||||
if (addonEventCallback_) {
|
||||
addonEventCallback_("SPELLS_CHANGED", {});
|
||||
addonEventCallback_("LEARNED_SPELL_IN_TAB", {});
|
||||
}
|
||||
}
|
||||
|
||||
void GameHandler::handleCastFailed(network::Packet& packet) {
|
||||
|
|
@ -19363,6 +19394,13 @@ void GameHandler::handleCastFailed(network::Packet& packet) {
|
|||
if (auto* sfx = renderer->getUiSoundManager())
|
||||
sfx->playError();
|
||||
}
|
||||
|
||||
// Fire UNIT_SPELLCAST_FAILED + UNIT_SPELLCAST_STOP so Lua addons can react
|
||||
if (addonEventCallback_) {
|
||||
addonEventCallback_("UNIT_SPELLCAST_FAILED", {"player", std::to_string(data.spellId)});
|
||||
addonEventCallback_("UNIT_SPELLCAST_STOP", {"player", std::to_string(data.spellId)});
|
||||
}
|
||||
if (spellCastFailedCallback_) spellCastFailedCallback_(data.spellId);
|
||||
}
|
||||
|
||||
static audio::SpellSoundManager::MagicSchool schoolMaskToMagicSchool(uint32_t mask) {
|
||||
|
|
@ -19383,6 +19421,7 @@ void GameHandler::handleSpellStart(network::Packet& packet) {
|
|||
if (data.casterUnit != playerGuid && data.castTime > 0) {
|
||||
auto& s = unitCastStates_[data.casterUnit];
|
||||
s.casting = true;
|
||||
s.isChannel = false;
|
||||
s.spellId = data.spellId;
|
||||
s.timeTotal = data.castTime / 1000.0f;
|
||||
s.timeRemaining = s.timeTotal;
|
||||
|
|
@ -23024,6 +23063,62 @@ void GameHandler::loadSkillLineAbilityDbc() {
|
|||
}
|
||||
}
|
||||
|
||||
const std::vector<GameHandler::SpellBookTab>& GameHandler::getSpellBookTabs() {
|
||||
// Rebuild when spell count changes (learns/unlearns)
|
||||
static size_t lastSpellCount = 0;
|
||||
if (lastSpellCount == knownSpells.size() && !spellBookTabsDirty_)
|
||||
return spellBookTabs_;
|
||||
lastSpellCount = knownSpells.size();
|
||||
spellBookTabsDirty_ = false;
|
||||
spellBookTabs_.clear();
|
||||
|
||||
static constexpr uint32_t SKILLLINE_CATEGORY_CLASS = 7;
|
||||
|
||||
// Group known spells by class skill line
|
||||
std::map<uint32_t, std::vector<uint32_t>> bySkillLine;
|
||||
std::vector<uint32_t> general;
|
||||
|
||||
for (uint32_t spellId : knownSpells) {
|
||||
auto slIt = spellToSkillLine_.find(spellId);
|
||||
if (slIt != spellToSkillLine_.end()) {
|
||||
uint32_t skillLineId = slIt->second;
|
||||
auto catIt = skillLineCategories_.find(skillLineId);
|
||||
if (catIt != skillLineCategories_.end() && catIt->second == SKILLLINE_CATEGORY_CLASS) {
|
||||
bySkillLine[skillLineId].push_back(spellId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
general.push_back(spellId);
|
||||
}
|
||||
|
||||
// Sort spells within each group by name
|
||||
auto byName = [this](uint32_t a, uint32_t b) {
|
||||
return getSpellName(a) < getSpellName(b);
|
||||
};
|
||||
|
||||
// "General" tab first (spells not in a class skill line)
|
||||
if (!general.empty()) {
|
||||
std::sort(general.begin(), general.end(), byName);
|
||||
spellBookTabs_.push_back({"General", "Interface\\Icons\\INV_Misc_Book_09", std::move(general)});
|
||||
}
|
||||
|
||||
// Class skill line tabs, sorted by name
|
||||
std::vector<std::pair<std::string, std::vector<uint32_t>>> named;
|
||||
for (auto& [skillLineId, spells] : bySkillLine) {
|
||||
auto nameIt = skillLineNames_.find(skillLineId);
|
||||
std::string tabName = (nameIt != skillLineNames_.end()) ? nameIt->second : "Unknown";
|
||||
std::sort(spells.begin(), spells.end(), byName);
|
||||
named.emplace_back(std::move(tabName), std::move(spells));
|
||||
}
|
||||
std::sort(named.begin(), named.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
|
||||
|
||||
for (auto& [name, spells] : named) {
|
||||
spellBookTabs_.push_back({std::move(name), "Interface\\Icons\\INV_Misc_Book_09", std::move(spells)});
|
||||
}
|
||||
|
||||
return spellBookTabs_;
|
||||
}
|
||||
|
||||
void GameHandler::categorizeTrainerSpells() {
|
||||
trainerTabs_.clear();
|
||||
|
||||
|
|
@ -23570,6 +23665,12 @@ void GameHandler::handleNewWorld(network::Packet& packet) {
|
|||
if (worldEntryCallback_) {
|
||||
worldEntryCallback_(mapId, serverX, serverY, serverZ, isSameMap);
|
||||
}
|
||||
|
||||
// Fire PLAYER_ENTERING_WORLD for teleports / zone transitions
|
||||
if (addonEventCallback_) {
|
||||
addonEventCallback_("PLAYER_ENTERING_WORLD", {"0"});
|
||||
addonEventCallback_("ZONE_CHANGED_NEW_AREA", {});
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -861,7 +861,7 @@ void WardenMemory::verifyWardenScanEntries() {
|
|||
bool WardenMemory::searchCodePattern(const uint8_t seed[4], const uint8_t expectedHash[20],
|
||||
uint8_t patternLen, bool imageOnly,
|
||||
uint32_t hintOffset, bool hintOnly) const {
|
||||
if (!loaded_ || patternLen == 0 || patternLen > 255) return false;
|
||||
if (!loaded_ || patternLen == 0) return false;
|
||||
|
||||
// Build cache key from all inputs: seed(4) + hash(20) + patLen(1) + imageOnly(1)
|
||||
std::string cacheKey(26, '\0');
|
||||
|
|
|
|||
|
|
@ -74,8 +74,7 @@ bool WardenModule::load(const std::vector<uint8_t>& moduleData,
|
|||
|
||||
// Step 3: Verify RSA signature
|
||||
if (!verifyRSASignature(decryptedData_)) {
|
||||
LOG_ERROR("WardenModule: RSA signature verification failed!");
|
||||
// Note: Currently returns true (skipping verification) due to placeholder modulus
|
||||
// Expected with placeholder modulus — verification is skipped gracefully
|
||||
}
|
||||
|
||||
// Step 4: Strip RSA signature (last 256 bytes) then zlib decompress
|
||||
|
|
@ -126,7 +125,7 @@ bool WardenModule::load(const std::vector<uint8_t>& moduleData,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WardenModule::processCheckRequest(const std::vector<uint8_t>& checkData,
|
||||
bool WardenModule::processCheckRequest([[maybe_unused]] const std::vector<uint8_t>& checkData,
|
||||
[[maybe_unused]] std::vector<uint8_t>& responseOut) {
|
||||
if (!loaded_) {
|
||||
LOG_ERROR("WardenModule: Module not loaded, cannot process checks");
|
||||
|
|
@ -427,12 +426,11 @@ bool WardenModule::verifyRSASignature(const std::vector<uint8_t>& data) {
|
|||
}
|
||||
}
|
||||
|
||||
LOG_ERROR("WardenModule: RSA signature verification FAILED (hash mismatch)");
|
||||
LOG_ERROR("WardenModule: NOTE: Using placeholder modulus - extract real modulus from WoW.exe for actual verification");
|
||||
LOG_WARNING("WardenModule: RSA signature verification skipped (placeholder modulus)");
|
||||
LOG_WARNING("WardenModule: Extract real modulus from WoW.exe for actual verification");
|
||||
|
||||
// For development, return true to proceed (since we don't have real modulus)
|
||||
// TODO: Set to false once real modulus is extracted
|
||||
LOG_WARNING("WardenModule: Skipping RSA verification (placeholder modulus)");
|
||||
return true; // TEMPORARY - change to false for production
|
||||
}
|
||||
|
||||
|
|
@ -705,7 +703,7 @@ bool WardenModule::parseExecutableFormat(const std::vector<uint8_t>& exeData) {
|
|||
std::memcpy(moduleMemory_, exeData.data() + 4, rawCopySize);
|
||||
}
|
||||
relocDataOffset_ = 0;
|
||||
LOG_ERROR("WardenModule: Could not parse copy/skip pairs (all known layouts failed); using raw payload fallback");
|
||||
LOG_WARNING("WardenModule: Could not parse copy/skip pairs (all known layouts failed); using raw payload fallback");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -832,7 +832,7 @@ void MovementPacket::writeMovementPayload(network::Packet& packet, const Movemen
|
|||
packet.writeUInt8(static_cast<uint8_t>(info.transportSeat));
|
||||
|
||||
// Optional second transport time for interpolated movement.
|
||||
if (info.flags2 & 0x0200) {
|
||||
if (info.flags2 & 0x0400) { // MOVEMENTFLAG2_INTERPOLATED_MOVEMENT
|
||||
packet.writeUInt32(info.transportTime2);
|
||||
}
|
||||
}
|
||||
|
|
@ -994,26 +994,27 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
|
|||
LOG_DEBUG(" OnTransport: guid=0x", std::hex, block.transportGuid, std::dec,
|
||||
" offset=(", block.transportX, ", ", block.transportY, ", ", block.transportZ, ")");
|
||||
|
||||
if (moveFlags2 & 0x0200) { // MOVEMENTFLAG2_INTERPOLATED_MOVEMENT
|
||||
if (moveFlags2 & 0x0400) { // MOVEMENTFLAG2_INTERPOLATED_MOVEMENT
|
||||
if (rem() < 4) return false;
|
||||
/*uint32_t tTime2 =*/ packet.readUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
// Swimming/flying pitch
|
||||
// WotLK 3.3.5a movement flags relevant here:
|
||||
// WotLK 3.3.5a movement flags (wire format):
|
||||
// SWIMMING = 0x00200000
|
||||
// FLYING = 0x01000000 (player/creature actively flying)
|
||||
// SPLINE_ELEVATION = 0x02000000 (smooth vertical spline offset — no pitch field)
|
||||
// CAN_FLY = 0x01000000 (ability to fly — no pitch field)
|
||||
// FLYING = 0x02000000 (actively flying — has pitch field)
|
||||
// SPLINE_ELEVATION = 0x04000000 (smooth vertical spline offset)
|
||||
// MovementFlags2:
|
||||
// MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING = 0x0010
|
||||
// MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING = 0x0020
|
||||
//
|
||||
// Pitch is present when SWIMMING or FLYING are set, or the always-allow flag is set.
|
||||
// The original code checked 0x02000000 (SPLINE_ELEVATION) which neither covers SWIMMING
|
||||
// nor FLYING, causing misaligned reads for swimming/flying entities in SMSG_UPDATE_OBJECT.
|
||||
// Note: CAN_FLY (0x01000000) does NOT gate pitch; only FLYING (0x02000000) does.
|
||||
// (TBC uses 0x01000000 for FLYING — see TbcMoveFlags in packet_parsers_tbc.cpp.)
|
||||
if ((moveFlags & 0x00200000) /* SWIMMING */ ||
|
||||
(moveFlags & 0x01000000) /* FLYING */ ||
|
||||
(moveFlags2 & 0x0010) /* MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING */) {
|
||||
(moveFlags & 0x02000000) /* FLYING */ ||
|
||||
(moveFlags2 & 0x0020) /* MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING */) {
|
||||
if (rem() < 4) return false;
|
||||
/*float pitch =*/ packet.readFloat();
|
||||
}
|
||||
|
|
|
|||
23
src/main.cpp
23
src/main.cpp
|
|
@ -8,6 +8,8 @@
|
|||
#include <SDL2/SDL.h>
|
||||
#ifdef __linux__
|
||||
#include <X11/Xlib.h>
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Keep a persistent X11 connection for emergency mouse release in signal handlers.
|
||||
// XOpenDisplay inside a signal handler is unreliable, so we open it once at startup.
|
||||
|
|
@ -26,6 +28,27 @@ static void releaseMouseGrab() {}
|
|||
|
||||
static void crashHandler(int sig) {
|
||||
releaseMouseGrab();
|
||||
#ifdef __linux__
|
||||
// Dump backtrace to debug log
|
||||
{
|
||||
void* frames[64];
|
||||
int n = backtrace(frames, 64);
|
||||
const char* sigName = (sig == SIGSEGV) ? "SIGSEGV" :
|
||||
(sig == SIGABRT) ? "SIGABRT" :
|
||||
(sig == SIGFPE) ? "SIGFPE" : "UNKNOWN";
|
||||
// Write to stderr and to the debug log file
|
||||
fprintf(stderr, "\n=== CRASH: signal %s (%d) ===\n", sigName, sig);
|
||||
backtrace_symbols_fd(frames, n, STDERR_FILENO);
|
||||
FILE* f = fopen("/tmp/wowee_debug.log", "a");
|
||||
if (f) {
|
||||
fprintf(f, "\n=== CRASH: signal %s (%d) ===\n", sigName, sig);
|
||||
fflush(f);
|
||||
// Also write backtrace to the log file fd
|
||||
backtrace_symbols_fd(frames, n, fileno(f));
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
std::signal(sig, SIG_DFL);
|
||||
std::raise(sig);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -462,6 +462,17 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender,
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Single layer (body skin only, no face/underwear overlays) — load directly
|
||||
VkTexture* skinTex = charRenderer_->loadTexture(bodySkinPath_);
|
||||
if (skinTex != nullptr) {
|
||||
for (size_t ti = 0; ti < model.textures.size(); ti++) {
|
||||
if (model.textures[ti].type == 1) {
|
||||
charRenderer_->setModelTexture(PREVIEW_MODEL_ID, static_cast<uint32_t>(ti), skinTex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ namespace rendering {
|
|||
|
||||
namespace {
|
||||
|
||||
// Shared lava UV scroll timer — ensures consistent animation across all render passes
|
||||
const auto kLavaAnimStart = std::chrono::steady_clock::now();
|
||||
|
||||
bool envFlagEnabled(const char* key, bool defaultValue) {
|
||||
const char* raw = std::getenv(key);
|
||||
if (!raw || !*raw) return defaultValue;
|
||||
|
|
@ -366,6 +369,41 @@ bool M2Renderer::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout
|
|||
vkCreateDescriptorPool(device, &ci, nullptr, &boneDescPool_);
|
||||
}
|
||||
|
||||
// Create a small identity-bone SSBO + descriptor set so that non-animated
|
||||
// draws always have a valid set 2 bound. The Intel ANV driver segfaults
|
||||
// on vkCmdDrawIndexed when a declared descriptor set slot is unbound.
|
||||
{
|
||||
// Single identity matrix (bone 0 = identity)
|
||||
glm::mat4 identity(1.0f);
|
||||
VkBufferCreateInfo bci{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
|
||||
bci.size = sizeof(glm::mat4);
|
||||
bci.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
VmaAllocationCreateInfo aci{};
|
||||
aci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
|
||||
aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
VmaAllocationInfo allocInfo{};
|
||||
vmaCreateBuffer(ctx->getAllocator(), &bci, &aci,
|
||||
&dummyBoneBuffer_, &dummyBoneAlloc_, &allocInfo);
|
||||
if (allocInfo.pMappedData) {
|
||||
memcpy(allocInfo.pMappedData, &identity, sizeof(identity));
|
||||
}
|
||||
|
||||
dummyBoneSet_ = allocateBoneSet();
|
||||
if (dummyBoneSet_) {
|
||||
VkDescriptorBufferInfo bufInfo{};
|
||||
bufInfo.buffer = dummyBoneBuffer_;
|
||||
bufInfo.offset = 0;
|
||||
bufInfo.range = sizeof(glm::mat4);
|
||||
VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
||||
write.dstSet = dummyBoneSet_;
|
||||
write.dstBinding = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
write.pBufferInfo = &bufInfo;
|
||||
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Pipeline layouts ---
|
||||
|
||||
// Main M2 pipeline layout: set 0 = perFrame, set 1 = material, set 2 = bones
|
||||
|
|
@ -746,6 +784,9 @@ void M2Renderer::shutdown() {
|
|||
if (ribbonPipelineLayout_) { vkDestroyPipelineLayout(device, ribbonPipelineLayout_, nullptr); ribbonPipelineLayout_ = VK_NULL_HANDLE; }
|
||||
|
||||
// Destroy descriptor pools and layouts
|
||||
if (dummyBoneBuffer_) { vmaDestroyBuffer(alloc, dummyBoneBuffer_, dummyBoneAlloc_); dummyBoneBuffer_ = VK_NULL_HANDLE; }
|
||||
// dummyBoneSet_ is freed implicitly when boneDescPool_ is destroyed
|
||||
dummyBoneSet_ = VK_NULL_HANDLE;
|
||||
if (materialDescPool_) { vkDestroyDescriptorPool(device, materialDescPool_, nullptr); materialDescPool_ = VK_NULL_HANDLE; }
|
||||
if (boneDescPool_) { vkDestroyDescriptorPool(device, boneDescPool_, nullptr); boneDescPool_ = VK_NULL_HANDLE; }
|
||||
if (materialSetLayout_) { vkDestroyDescriptorSetLayout(device, materialSetLayout_, nullptr); materialSetLayout_ = VK_NULL_HANDLE; }
|
||||
|
|
@ -812,7 +853,11 @@ VkDescriptorSet M2Renderer::allocateMaterialSet() {
|
|||
ai.descriptorSetCount = 1;
|
||||
ai.pSetLayouts = &materialSetLayout_;
|
||||
VkDescriptorSet set = VK_NULL_HANDLE;
|
||||
vkAllocateDescriptorSets(vkCtx_->getDevice(), &ai, &set);
|
||||
VkResult result = vkAllocateDescriptorSets(vkCtx_->getDevice(), &ai, &set);
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("M2Renderer: material descriptor set allocation failed (", result, ")");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
|
|
@ -822,7 +867,11 @@ VkDescriptorSet M2Renderer::allocateBoneSet() {
|
|||
ai.descriptorSetCount = 1;
|
||||
ai.pSetLayouts = &boneSetLayout_;
|
||||
VkDescriptorSet set = VK_NULL_HANDLE;
|
||||
vkAllocateDescriptorSets(vkCtx_->getDevice(), &ai, &set);
|
||||
VkResult result = vkAllocateDescriptorSets(vkCtx_->getDevice(), &ai, &set);
|
||||
if (result != VK_SUCCESS) {
|
||||
LOG_ERROR("M2Renderer: bone descriptor set allocation failed (", result, ")");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
|
|
@ -1303,6 +1352,10 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
|||
gpuModel.indexBuffer = buf.buffer;
|
||||
gpuModel.indexAlloc = buf.allocation;
|
||||
}
|
||||
|
||||
if (!gpuModel.vertexBuffer || !gpuModel.indexBuffer) {
|
||||
LOG_ERROR("M2Renderer::loadModel: GPU buffer upload failed for model ", modelId);
|
||||
}
|
||||
}
|
||||
|
||||
// Load ALL textures from the model into a local vector.
|
||||
|
|
@ -1751,6 +1804,7 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
|||
}
|
||||
|
||||
models[modelId] = std::move(gpuModel);
|
||||
spatialIndexDirty_ = true; // Map may have rehashed — refresh cachedModel pointers
|
||||
|
||||
LOG_DEBUG("Loaded M2 model: ", model.name, " (", models[modelId].vertexCount, " vertices, ",
|
||||
models[modelId].indexCount / 3, " triangles, ", models[modelId].batches.size(), " batches)");
|
||||
|
|
@ -2504,6 +2558,7 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
|
||||
uint32_t currentModelId = UINT32_MAX;
|
||||
const M2ModelGPU* currentModel = nullptr;
|
||||
bool currentModelValid = false;
|
||||
|
||||
// State tracking
|
||||
VkPipeline currentPipeline = VK_NULL_HANDLE;
|
||||
|
|
@ -2519,6 +2574,12 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
float fadeAlpha;
|
||||
};
|
||||
|
||||
// Validate per-frame descriptor set before any Vulkan commands
|
||||
if (!perFrameSet) {
|
||||
LOG_ERROR("M2Renderer::render: perFrameSet is VK_NULL_HANDLE — skipping M2 render");
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind per-frame descriptor set (set 0) — shared across all draws
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipelineLayout_, 0, 1, &perFrameSet, 0, nullptr);
|
||||
|
|
@ -2528,6 +2589,13 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
currentPipeline = opaquePipeline_;
|
||||
bool opaquePass = true; // Pass 1 = opaque, pass 2 = transparent (set below for second pass)
|
||||
|
||||
// Bind dummy bone set (set 2) so non-animated draws have a valid binding.
|
||||
// Animated instances override this with their real bone set per-instance.
|
||||
if (dummyBoneSet_) {
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipelineLayout_, 2, 1, &dummyBoneSet_, 0, nullptr);
|
||||
}
|
||||
|
||||
for (const auto& entry : sortedVisible_) {
|
||||
if (entry.index >= instances.size()) continue;
|
||||
auto& instance = instances[entry.index];
|
||||
|
|
@ -2535,14 +2603,17 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
// Bind vertex + index buffers once per model group
|
||||
if (entry.modelId != currentModelId) {
|
||||
currentModelId = entry.modelId;
|
||||
currentModelValid = false;
|
||||
auto mdlIt = models.find(currentModelId);
|
||||
if (mdlIt == models.end()) continue;
|
||||
currentModel = &mdlIt->second;
|
||||
if (!currentModel->vertexBuffer) continue;
|
||||
if (!currentModel->vertexBuffer || !currentModel->indexBuffer) continue;
|
||||
currentModelValid = true;
|
||||
VkDeviceSize offset = 0;
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, ¤tModel->vertexBuffer, &offset);
|
||||
vkCmdBindIndexBuffer(cmd, currentModel->indexBuffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
}
|
||||
if (!currentModelValid) continue;
|
||||
|
||||
const M2ModelGPU& model = *currentModel;
|
||||
|
||||
|
|
@ -2697,10 +2768,10 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
}
|
||||
}
|
||||
}
|
||||
// Lava M2 models: fallback UV scroll if no texture animation
|
||||
// Lava M2 models: fallback UV scroll if no texture animation.
|
||||
// Uses kLavaAnimStart (file-scope) for consistent timing across passes.
|
||||
if (model.isLavaModel && uvOffset == glm::vec2(0.0f)) {
|
||||
static auto startTime = std::chrono::steady_clock::now();
|
||||
float t = std::chrono::duration<float>(std::chrono::steady_clock::now() - startTime).count();
|
||||
float t = std::chrono::duration<float>(std::chrono::steady_clock::now() - kLavaAnimStart).count();
|
||||
uvOffset = glm::vec2(t * 0.03f, -t * 0.08f);
|
||||
}
|
||||
|
||||
|
|
@ -2785,7 +2856,6 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
continue;
|
||||
}
|
||||
vkCmdPushConstants(cmd, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(pc), &pc);
|
||||
|
||||
vkCmdDrawIndexed(cmd, batch.indexCount, 1, batch.indexStart, 0, 0);
|
||||
lastDrawCallCount++;
|
||||
}
|
||||
|
|
@ -2799,6 +2869,7 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
|
||||
currentModelId = UINT32_MAX;
|
||||
currentModel = nullptr;
|
||||
currentModelValid = false;
|
||||
// Reset pipeline to opaque so the first transparent bind always sets explicitly
|
||||
currentPipeline = opaquePipeline_;
|
||||
|
||||
|
|
@ -2817,14 +2888,17 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
// `!opaquePass && !rawTransparent → continue` handles opaque skipping)
|
||||
if (entry.modelId != currentModelId) {
|
||||
currentModelId = entry.modelId;
|
||||
currentModelValid = false;
|
||||
auto mdlIt = models.find(currentModelId);
|
||||
if (mdlIt == models.end()) continue;
|
||||
currentModel = &mdlIt->second;
|
||||
if (!currentModel->vertexBuffer) continue;
|
||||
if (!currentModel->vertexBuffer || !currentModel->indexBuffer) continue;
|
||||
currentModelValid = true;
|
||||
VkDeviceSize offset = 0;
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, ¤tModel->vertexBuffer, &offset);
|
||||
vkCmdBindIndexBuffer(cmd, currentModel->indexBuffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
}
|
||||
if (!currentModelValid) continue;
|
||||
|
||||
const M2ModelGPU& model = *currentModel;
|
||||
|
||||
|
|
@ -2910,8 +2984,7 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
}
|
||||
}
|
||||
if (model.isLavaModel && uvOffset == glm::vec2(0.0f)) {
|
||||
static auto startTime2 = std::chrono::steady_clock::now();
|
||||
float t = std::chrono::duration<float>(std::chrono::steady_clock::now() - startTime2).count();
|
||||
float t = std::chrono::duration<float>(std::chrono::steady_clock::now() - kLavaAnimStart).count();
|
||||
uvOffset = glm::vec2(t * 0.03f, -t * 0.08f);
|
||||
}
|
||||
|
||||
|
|
@ -4168,6 +4241,21 @@ void M2Renderer::clear() {
|
|||
}
|
||||
if (boneDescPool_) {
|
||||
vkResetDescriptorPool(device, boneDescPool_, 0);
|
||||
// Re-allocate the dummy bone set (invalidated by pool reset)
|
||||
dummyBoneSet_ = allocateBoneSet();
|
||||
if (dummyBoneSet_ && dummyBoneBuffer_) {
|
||||
VkDescriptorBufferInfo bufInfo{};
|
||||
bufInfo.buffer = dummyBoneBuffer_;
|
||||
bufInfo.offset = 0;
|
||||
bufInfo.range = sizeof(glm::mat4);
|
||||
VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
||||
write.dstSet = dummyBoneSet_;
|
||||
write.dstBinding = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
write.pBufferInfo = &bufInfo;
|
||||
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
models.clear();
|
||||
|
|
|
|||
|
|
@ -562,7 +562,17 @@ std::shared_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
|
|||
|
||||
// Pre-load WMO doodads (M2 models inside WMO)
|
||||
if (!workerRunning.load()) return nullptr;
|
||||
if (!wmoModel.doodadSets.empty() && !wmoModel.doodads.empty()) {
|
||||
|
||||
// Skip WMO doodads if this placement was already prepared by another tile's worker.
|
||||
// This prevents 15+ copies of Stormwind's ~6000 doodads from being parsed
|
||||
// simultaneously, which was the primary cause of OOM during world load.
|
||||
bool wmoAlreadyPrepared = false;
|
||||
if (placement.uniqueId != 0) {
|
||||
std::lock_guard<std::mutex> lock(preparedWmoUniqueIdsMutex_);
|
||||
wmoAlreadyPrepared = !preparedWmoUniqueIds_.insert(placement.uniqueId).second;
|
||||
}
|
||||
|
||||
if (!wmoAlreadyPrepared && !wmoModel.doodadSets.empty() && !wmoModel.doodads.empty()) {
|
||||
glm::mat4 wmoMatrix(1.0f);
|
||||
wmoMatrix = glm::translate(wmoMatrix, pos);
|
||||
wmoMatrix = glm::rotate(wmoMatrix, rot.z, glm::vec3(0, 0, 1));
|
||||
|
|
@ -575,6 +585,7 @@ std::shared_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
|
|||
setsToLoad.push_back(placement.doodadSet);
|
||||
}
|
||||
std::unordered_set<uint32_t> loadedDoodadIndices;
|
||||
std::unordered_set<uint32_t> wmoPreparedModelIds; // within-WMO model dedup
|
||||
for (uint32_t setIdx : setsToLoad) {
|
||||
const auto& doodadSet = wmoModel.doodadSets[setIdx];
|
||||
for (uint32_t di = 0; di < doodadSet.count; di++) {
|
||||
|
|
@ -599,15 +610,16 @@ std::shared_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
|
|||
|
||||
uint32_t doodadModelId = static_cast<uint32_t>(std::hash<std::string>{}(m2Path));
|
||||
|
||||
// Skip file I/O if model already uploaded from a previous tile
|
||||
// Skip file I/O if model already uploaded or already prepared within this WMO
|
||||
bool modelAlreadyUploaded = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(uploadedM2IdsMutex_);
|
||||
modelAlreadyUploaded = uploadedM2Ids_.count(doodadModelId) > 0;
|
||||
}
|
||||
bool modelAlreadyPreparedInWmo = !wmoPreparedModelIds.insert(doodadModelId).second;
|
||||
|
||||
pipeline::M2Model m2Model;
|
||||
if (!modelAlreadyUploaded) {
|
||||
if (!modelAlreadyUploaded && !modelAlreadyPreparedInWmo) {
|
||||
std::vector<uint8_t> m2Data = assetManager->readFile(m2Path);
|
||||
if (m2Data.empty()) continue;
|
||||
|
||||
|
|
@ -1404,7 +1416,11 @@ void TerrainManager::unloadTile(int x, int y) {
|
|||
wmoRenderer->removeInstances(fit->wmoInstanceIds);
|
||||
}
|
||||
for (uint32_t uid : fit->tileUniqueIds) placedDoodadIds.erase(uid);
|
||||
for (uint32_t uid : fit->tileWmoUniqueIds) placedWmoIds.erase(uid);
|
||||
for (uint32_t uid : fit->tileWmoUniqueIds) {
|
||||
placedWmoIds.erase(uid);
|
||||
std::lock_guard<std::mutex> lock(preparedWmoUniqueIdsMutex_);
|
||||
preparedWmoUniqueIds_.erase(uid);
|
||||
}
|
||||
finalizingTiles_.erase(fit);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1425,6 +1441,8 @@ void TerrainManager::unloadTile(int x, int y) {
|
|||
}
|
||||
for (uint32_t uid : tile->wmoUniqueIds) {
|
||||
placedWmoIds.erase(uid);
|
||||
std::lock_guard<std::mutex> lock(preparedWmoUniqueIdsMutex_);
|
||||
preparedWmoUniqueIds_.erase(uid);
|
||||
}
|
||||
|
||||
// Remove M2 doodad instances
|
||||
|
|
@ -1509,6 +1527,10 @@ void TerrainManager::unloadAll() {
|
|||
std::lock_guard<std::mutex> lock(uploadedM2IdsMutex_);
|
||||
uploadedM2Ids_.clear();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(preparedWmoUniqueIdsMutex_);
|
||||
preparedWmoUniqueIds_.clear();
|
||||
}
|
||||
|
||||
LOG_INFO("Unloading all terrain tiles");
|
||||
loadedTiles.clear();
|
||||
|
|
@ -1561,6 +1583,10 @@ void TerrainManager::softReset() {
|
|||
std::lock_guard<std::mutex> lock(uploadedM2IdsMutex_);
|
||||
uploadedM2Ids_.clear();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(preparedWmoUniqueIdsMutex_);
|
||||
preparedWmoUniqueIds_.clear();
|
||||
}
|
||||
|
||||
// Clear tile cache — keys are (x,y) without map name, so stale entries from
|
||||
// a different map with overlapping coordinates would produce wrong geometry.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue