From 572bb4ef363ee4ae37b8ca8bf1ecb228360133c0 Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 22 Mar 2026 21:38:56 +0300 Subject: [PATCH 1/4] fix preview white textutes --- Data/expansions/classic/dbc_layouts.json | 274 +++++++++++++----- Data/expansions/tbc/dbc_layouts.json | 329 ++++++++++++++++----- Data/expansions/turtle/dbc_layouts.json | 316 ++++++++++++++++----- Data/expansions/wotlk/dbc_layouts.json | 346 ++++++++++++++++++----- src/rendering/character_preview.cpp | 14 +- 5 files changed, 984 insertions(+), 295 deletions(-) diff --git a/Data/expansions/classic/dbc_layouts.json b/Data/expansions/classic/dbc_layouts.json index e5d0793f..a3cba2f8 100644 --- a/Data/expansions/classic/dbc_layouts.json +++ b/Data/expansions/classic/dbc_layouts.json @@ -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, - "Flags": 9 + "RaceID": 1, + "SexID": 2, + "BaseSection": 3, + "Texture1": 4, + "Texture2": 5, + "Texture3": 6, + "Flags": 7, + "VariationIndex": 8, + "ColorIndex": 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 } } diff --git a/Data/expansions/tbc/dbc_layouts.json b/Data/expansions/tbc/dbc_layouts.json index da2fb9a5..8dc4bbe8 100644 --- a/Data/expansions/tbc/dbc_layouts.json +++ b/Data/expansions/tbc/dbc_layouts.json @@ -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, - "Flags": 9 + "RaceID": 1, + "SexID": 2, + "BaseSection": 3, + "Texture1": 4, + "Texture2": 5, + "Texture3": 6, + "Flags": 7, + "VariationIndex": 8, + "ColorIndex": 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 } } diff --git a/Data/expansions/turtle/dbc_layouts.json b/Data/expansions/turtle/dbc_layouts.json index cb44c54a..b7650ef5 100644 --- a/Data/expansions/turtle/dbc_layouts.json +++ b/Data/expansions/turtle/dbc_layouts.json @@ -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, - "Flags": 9 + "RaceID": 1, + "SexID": 2, + "BaseSection": 3, + "Texture1": 4, + "Texture2": 5, + "Texture3": 6, + "Flags": 7, + "VariationIndex": 8, + "ColorIndex": 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 } } diff --git a/Data/expansions/wotlk/dbc_layouts.json b/Data/expansions/wotlk/dbc_layouts.json index 4ecbfc32..505ae150 100644 --- a/Data/expansions/wotlk/dbc_layouts.json +++ b/Data/expansions/wotlk/dbc_layouts.json @@ -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, - "Flags": 9 + "RaceID": 1, + "SexID": 2, + "BaseSection": 3, + "Texture1": 4, + "Texture2": 5, + "Texture3": 6, + "Flags": 7, + "VariationIndex": 8, + "ColorIndex": 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 } } diff --git a/src/rendering/character_preview.cpp b/src/rendering/character_preview.cpp index 2cb6278e..31546026 100644 --- a/src/rendering/character_preview.cpp +++ b/src/rendering/character_preview.cpp @@ -336,8 +336,8 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, uint32_t fRace = csL ? (*csL)["RaceID"] : 1; uint32_t fSex = csL ? (*csL)["SexID"] : 2; uint32_t fBase = csL ? (*csL)["BaseSection"] : 3; - uint32_t fVar = csL ? (*csL)["VariationIndex"] : 4; - uint32_t fColor = csL ? (*csL)["ColorIndex"] : 5; + uint32_t fVar = csL ? (*csL)["VariationIndex"] : 8; + uint32_t fColor = csL ? (*csL)["ColorIndex"] : 9; for (uint32_t r = 0; r < charSectionsDbc->getRecordCount(); r++) { uint32_t raceId = charSectionsDbc->getUInt32(r, fRace); uint32_t sexId = charSectionsDbc->getUInt32(r, fSex); @@ -350,7 +350,7 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, // Section 0: Body skin (variation=0, colorIndex = skin color) if (baseSection == 0 && !foundSkin && variationIndex == 0 && colorIndex == static_cast(skin)) { - std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 6); + std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 4); if (!tex1.empty()) { bodySkinPath_ = tex1; foundSkin = true; @@ -360,8 +360,8 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, else if (baseSection == 1 && !foundFace && variationIndex == static_cast(face) && colorIndex == static_cast(skin)) { - std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 6); - std::string tex2 = charSectionsDbc->getString(r, csL ? (*csL)["Texture2"] : 7); + std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 4); + std::string tex2 = charSectionsDbc->getString(r, csL ? (*csL)["Texture2"] : 5); if (!tex1.empty()) faceLowerPath = tex1; if (!tex2.empty()) faceUpperPath = tex2; foundFace = true; @@ -370,7 +370,7 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, else if (baseSection == 3 && !foundHair && variationIndex == static_cast(hairStyle) && colorIndex == static_cast(hairColor)) { - std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 6); + std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 4); if (!tex1.empty()) { hairScalpPath = tex1; foundHair = true; @@ -379,7 +379,7 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, // Section 4: Underwear (variation=0, colorIndex = skin color) else if (baseSection == 4 && !foundUnderwear && variationIndex == 0 && colorIndex == static_cast(skin)) { - uint32_t texBase = csL ? (*csL)["Texture1"] : 6; + uint32_t texBase = csL ? (*csL)["Texture1"] : 4; for (uint32_t f = texBase; f <= texBase + 2; f++) { std::string tex = charSectionsDbc->getString(r, f); if (!tex.empty()) { From bd725f0bbe5da03091e1d16a414809297e6b579d Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 22 Mar 2026 21:39:40 +0300 Subject: [PATCH 2/4] build fix --- src/addons/lua_engine.cpp | 1 + src/game/warden_memory.cpp | 2 +- src/game/warden_module.cpp | 12 +++++------- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/addons/lua_engine.cpp b/src/addons/lua_engine.cpp index 6148ddd0..5eccdf36 100644 --- a/src/addons/lua_engine.cpp +++ b/src/addons/lua_engine.cpp @@ -3869,6 +3869,7 @@ void LuaEngine::registerCoreAPI() { "function StopSound() end\n" "function UIParent_OnEvent() end\n" "UIParent = CreateFrame('Frame', 'UIParent')\n" + "UIPanelWindows = {}\n" "WorldFrame = CreateFrame('Frame', 'WorldFrame')\n" // GameTooltip: global tooltip frame used by virtually all addons "GameTooltip = CreateFrame('Frame', 'GameTooltip')\n" diff --git a/src/game/warden_memory.cpp b/src/game/warden_memory.cpp index 33127e2c..5b13456a 100644 --- a/src/game/warden_memory.cpp +++ b/src/game/warden_memory.cpp @@ -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'); diff --git a/src/game/warden_module.cpp b/src/game/warden_module.cpp index eea0f0ee..bf44c26e 100644 --- a/src/game/warden_module.cpp +++ b/src/game/warden_module.cpp @@ -74,8 +74,7 @@ bool WardenModule::load(const std::vector& 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& moduleData, return true; } -bool WardenModule::processCheckRequest(const std::vector& checkData, +bool WardenModule::processCheckRequest([[maybe_unused]] const std::vector& checkData, [[maybe_unused]] std::vector& responseOut) { if (!loaded_) { LOG_ERROR("WardenModule: Module not loaded, cannot process checks"); @@ -427,12 +426,11 @@ bool WardenModule::verifyRSASignature(const std::vector& 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& 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; } From 7565019dc9e445a583d4fa350d9e6dc0288aef7f Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 22 Mar 2026 21:40:16 +0300 Subject: [PATCH 3/4] log falling --- src/main.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index d3811b3b..8ae707e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,8 @@ #include #ifdef __linux__ #include +#include +#include // 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); } From 027640189a5eb3d6670af2b529d843b8695d9ea2 Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 22 Mar 2026 21:47:12 +0300 Subject: [PATCH 4/4] make start on ubuntu intel video cards --- include/game/world_packets.hpp | 7 +- include/rendering/m2_renderer.hpp | 7 ++ include/rendering/terrain_manager.hpp | 5 ++ src/core/application.cpp | 36 +++++----- src/game/world_packets.cpp | 21 +++--- src/rendering/m2_renderer.cpp | 96 +++++++++++++++++++++++++-- src/rendering/terrain_manager.cpp | 34 ++++++++-- src/ui/character_create_screen.cpp | 8 +-- 8 files changed, 170 insertions(+), 44 deletions(-) diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index c0408743..d72aebe6 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -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, }; /** diff --git a/include/rendering/m2_renderer.hpp b/include/rendering/m2_renderer.hpp index 08d83d32..c50dfb0f 100644 --- a/include/rendering/m2_renderer.hpp +++ b/include/rendering/m2_renderer.hpp @@ -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; diff --git a/include/rendering/terrain_manager.hpp b/include/rendering/terrain_manager.hpp index 9fa540b3..ab6e881f 100644 --- a/include/rendering/terrain_manager.hpp +++ b/include/rendering/terrain_manager.hpp @@ -394,6 +394,11 @@ private: std::unordered_set 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 preparedWmoUniqueIds_; + std::mutex preparedWmoUniqueIdsMutex_; + // Dedup set for doodad placements across tile boundaries std::unordered_set placedDoodadIds; diff --git a/src/core/application.cpp b/src/core/application.cpp index c5e7dccb..a4728379 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -3671,13 +3671,13 @@ void Application::spawnPlayerCharacter() { uint32_t raceId = charSectionsDbc->getUInt32(r, csL ? (*csL)["RaceID"] : 1); uint32_t sexId = 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 (raceId != targetRaceId || sexId != targetSexId) continue; // Section 0 = skin: match by colorIndex = skin byte - const uint32_t csTex1 = csL ? (*csL)["Texture1"] : 6; + const uint32_t csTex1 = csL ? (*csL)["Texture1"] : 4; if (baseSection == 0 && !foundSkin && colorIndex == charSkinId) { std::string tex1 = charSectionsDbc->getString(r, csTex1); if (!tex1.empty()) { @@ -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(extraCopy.hairStyleId)) continue; if (colorIdx != static_cast(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); diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 27051cb2..e740ea4c 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -832,7 +832,7 @@ void MovementPacket::writeMovementPayload(network::Packet& packet, const Movemen packet.writeUInt8(static_cast(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(); } diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index f711f542..b4bfa439 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -366,6 +366,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 +781,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 +850,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 +864,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 +1349,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 +1801,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 +2555,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 +2571,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 +2586,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 +2600,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; @@ -2785,7 +2853,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 +2866,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 +2885,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; @@ -4168,6 +4239,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(); diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index f380cc65..ba929d7c 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -562,7 +562,17 @@ std::shared_ptr 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 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 TerrainManager::prepareTile(int x, int y) { setsToLoad.push_back(placement.doodadSet); } std::unordered_set loadedDoodadIndices; + std::unordered_set 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 TerrainManager::prepareTile(int x, int y) { uint32_t doodadModelId = static_cast(std::hash{}(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 lock(uploadedM2IdsMutex_); modelAlreadyUploaded = uploadedM2Ids_.count(doodadModelId) > 0; } + bool modelAlreadyPreparedInWmo = !wmoPreparedModelIds.insert(doodadModelId).second; pipeline::M2Model m2Model; - if (!modelAlreadyUploaded) { + if (!modelAlreadyUploaded && !modelAlreadyPreparedInWmo) { std::vector 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 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 lock(preparedWmoUniqueIdsMutex_); + preparedWmoUniqueIds_.erase(uid); } // Remove M2 doodad instances @@ -1509,6 +1527,10 @@ void TerrainManager::unloadAll() { std::lock_guard lock(uploadedM2IdsMutex_); uploadedM2Ids_.clear(); } + { + std::lock_guard lock(preparedWmoUniqueIdsMutex_); + preparedWmoUniqueIds_.clear(); + } LOG_INFO("Unloading all terrain tiles"); loadedTiles.clear(); @@ -1561,6 +1583,10 @@ void TerrainManager::softReset() { std::lock_guard lock(uploadedM2IdsMutex_); uploadedM2Ids_.clear(); } + { + std::lock_guard 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. diff --git a/src/ui/character_create_screen.cpp b/src/ui/character_create_screen.cpp index fa81756f..63933924 100644 --- a/src/ui/character_create_screen.cpp +++ b/src/ui/character_create_screen.cpp @@ -257,8 +257,8 @@ void CharacterCreateScreen::updateAppearanceRanges() { if (raceId != targetRaceId || sexId != targetSexId) continue; uint32_t baseSection = dbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3); - uint32_t variationIndex = dbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4); - uint32_t colorIndex = dbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5); + uint32_t variationIndex = dbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8); + uint32_t colorIndex = dbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9); if (baseSection == 0 && variationIndex == 0) { skinMax = std::max(skinMax, static_cast(colorIndex)); @@ -284,8 +284,8 @@ void CharacterCreateScreen::updateAppearanceRanges() { if (raceId != targetRaceId || sexId != targetSexId) continue; uint32_t baseSection = dbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3); - uint32_t variationIndex = dbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4); - uint32_t colorIndex = dbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5); + uint32_t variationIndex = dbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8); + uint32_t colorIndex = dbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9); if (baseSection == 1 && colorIndex == static_cast(skin)) { faceMax = std::max(faceMax, static_cast(variationIndex));