diff --git a/spinui_glass/AttackIndicator.tga b/spinui_glass/AttackIndicator.tga index fad2a9c..c071f50 100644 Binary files a/spinui_glass/AttackIndicator.tga and b/spinui_glass/AttackIndicator.tga differ diff --git a/spinui_glass/EQUI_PlayerWindow.xml b/spinui_glass/EQUI_PlayerWindow.xml index a0e2bf1..4984b33 100644 --- a/spinui_glass/EQUI_PlayerWindow.xml +++ b/spinui_glass/EQUI_PlayerWindow.xml @@ -10,10 +10,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -28,7 +42,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -43,7 +57,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -58,7 +72,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -73,7 +87,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -88,7 +102,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -105,8 +119,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -120,8 +134,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -135,10 +149,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -150,9 +164,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -162,6 +176,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -845,7 +872,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1162,8 +1189,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1276,9 +1303,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1334,7 +1361,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1353,15 +1380,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_glass/EQUI_PlayerWindow1.xml b/spinui_glass/EQUI_PlayerWindow1.xml index 0504d81..4f370c8 100644 --- a/spinui_glass/EQUI_PlayerWindow1.xml +++ b/spinui_glass/EQUI_PlayerWindow1.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_glass/EQUI_PlayerWindow2.xml b/spinui_glass/EQUI_PlayerWindow2.xml index 0504d81..4f370c8 100644 --- a/spinui_glass/EQUI_PlayerWindow2.xml +++ b/spinui_glass/EQUI_PlayerWindow2.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_glass/EQUI_PlayerWindow3.xml b/spinui_glass/EQUI_PlayerWindow3.xml index 0504d81..4f370c8 100644 --- a/spinui_glass/EQUI_PlayerWindow3.xml +++ b/spinui_glass/EQUI_PlayerWindow3.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_glass/EQUI_PlayerWindow4.xml b/spinui_glass/EQUI_PlayerWindow4.xml index 0504d81..4f370c8 100644 --- a/spinui_glass/EQUI_PlayerWindow4.xml +++ b/spinui_glass/EQUI_PlayerWindow4.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_glass/EQUI_PlayerWindow5.xml b/spinui_glass/EQUI_PlayerWindow5.xml index 0504d81..4f370c8 100644 --- a/spinui_glass/EQUI_PlayerWindow5.xml +++ b/spinui_glass/EQUI_PlayerWindow5.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_glass/EQUI_PlayerWindow6.xml b/spinui_glass/EQUI_PlayerWindow6.xml index 0504d81..4f370c8 100644 --- a/spinui_glass/EQUI_PlayerWindow6.xml +++ b/spinui_glass/EQUI_PlayerWindow6.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_glass/SpinAttackPerimeter.tga b/spinui_glass/SpinAttackPerimeter.tga new file mode 100644 index 0000000..fb21406 Binary files /dev/null and b/spinui_glass/SpinAttackPerimeter.tga differ diff --git a/spinui_glass/SpinAttackRail.tga b/spinui_glass/SpinAttackRail.tga new file mode 100644 index 0000000..c071f50 Binary files /dev/null and b/spinui_glass/SpinAttackRail.tga differ diff --git a/spinui_reloaded/AttackIndicator.tga b/spinui_reloaded/AttackIndicator.tga index fad2a9c..c071f50 100644 Binary files a/spinui_reloaded/AttackIndicator.tga and b/spinui_reloaded/AttackIndicator.tga differ diff --git a/spinui_reloaded/EQUI_PlayerWindow.xml b/spinui_reloaded/EQUI_PlayerWindow.xml index 26a78b9..72b974b 100644 --- a/spinui_reloaded/EQUI_PlayerWindow.xml +++ b/spinui_reloaded/EQUI_PlayerWindow.xml @@ -10,10 +10,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -28,7 +42,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -43,7 +57,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -58,7 +72,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -73,7 +87,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -88,7 +102,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -105,8 +119,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -120,8 +134,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -135,10 +149,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -150,9 +164,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -162,6 +176,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -845,7 +872,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1162,8 +1189,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1276,9 +1303,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1334,7 +1361,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1353,15 +1380,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_reloaded/EQUI_PlayerWindow1.xml b/spinui_reloaded/EQUI_PlayerWindow1.xml index be90c4c..e6a0869 100644 --- a/spinui_reloaded/EQUI_PlayerWindow1.xml +++ b/spinui_reloaded/EQUI_PlayerWindow1.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_reloaded/EQUI_PlayerWindow2.xml b/spinui_reloaded/EQUI_PlayerWindow2.xml index be90c4c..e6a0869 100644 --- a/spinui_reloaded/EQUI_PlayerWindow2.xml +++ b/spinui_reloaded/EQUI_PlayerWindow2.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_reloaded/EQUI_PlayerWindow3.xml b/spinui_reloaded/EQUI_PlayerWindow3.xml index be90c4c..e6a0869 100644 --- a/spinui_reloaded/EQUI_PlayerWindow3.xml +++ b/spinui_reloaded/EQUI_PlayerWindow3.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_reloaded/EQUI_PlayerWindow4.xml b/spinui_reloaded/EQUI_PlayerWindow4.xml index be90c4c..e6a0869 100644 --- a/spinui_reloaded/EQUI_PlayerWindow4.xml +++ b/spinui_reloaded/EQUI_PlayerWindow4.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_reloaded/EQUI_PlayerWindow5.xml b/spinui_reloaded/EQUI_PlayerWindow5.xml index be90c4c..e6a0869 100644 --- a/spinui_reloaded/EQUI_PlayerWindow5.xml +++ b/spinui_reloaded/EQUI_PlayerWindow5.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_reloaded/EQUI_PlayerWindow6.xml b/spinui_reloaded/EQUI_PlayerWindow6.xml index be90c4c..e6a0869 100644 --- a/spinui_reloaded/EQUI_PlayerWindow6.xml +++ b/spinui_reloaded/EQUI_PlayerWindow6.xml @@ -12,10 +12,24 @@ + + + 128 + 32 + + + + + + 128 + 32 + + + false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -30,7 +44,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -45,7 +59,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -60,7 +74,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -75,7 +89,7 @@ false - AttackIndicator.tga + SpinAttackRail.tga 0 0 @@ -90,7 +104,7 @@ false - AttackIndicator.tga + SpinAttackPerimeter.tga 0 0 @@ -107,8 +121,8 @@ A_AttackIndicatorTop false true - 70 - 78 + 67 + 70 0 0 true @@ -122,8 +136,8 @@ A_AttackIndicatorBottom false true - 10 - 2 + 9 + 6 0 0 false @@ -137,10 +151,10 @@ A_AttackIndicatorLeft false true - 70 - 2 + 67 + 6 0 - 8 + 3 true false true @@ -152,9 +166,9 @@ A_AttackIndicatorRight false true - 70 - 2 - 8 + 67 + 6 + 3 0 true false @@ -164,6 +178,19 @@ A_AttackIndicatorAnimFill + A_AttackIndicatorFill + true + false + true + true + 70 + 6 + 0 + 0 + true + false + true + false @@ -847,7 +874,7 @@ A_GaugeLinesFill true - 84 + 83 94 1 1 @@ -1164,8 +1191,8 @@ false 2 2 - 72 - 2 + 70 + 0 false false true @@ -1278,9 +1305,9 @@ false false 70 - 2 - -1 - -1 + 9 + 3 + 3 Player_HP Pet_HP Player_Mana @@ -1335,7 +1362,7 @@ 193 280 - 174 + 193 false false WDT_RoundedTransparentNoArrow @@ -1354,15 +1381,15 @@ -9 Screen:PlayerSubWindow + DragBox:PW_DragBox + DragBox:PW_DragBox2 + DragBox:PWDragBox3 + PW_BuffWindow A_AttackIndicatorAnimTop A_AttackIndicatorAnimBottom A_AttackIndicatorAnimLeft A_AttackIndicatorAnimRight A_AttackIndicatorAnimFill - DragBox:PW_DragBox - DragBox:PW_DragBox2 - DragBox:PWDragBox3 - PW_BuffWindow diff --git a/spinui_reloaded/SpinAttackPerimeter.tga b/spinui_reloaded/SpinAttackPerimeter.tga new file mode 100644 index 0000000..fb21406 Binary files /dev/null and b/spinui_reloaded/SpinAttackPerimeter.tga differ diff --git a/spinui_reloaded/SpinAttackRail.tga b/spinui_reloaded/SpinAttackRail.tga new file mode 100644 index 0000000..c071f50 Binary files /dev/null and b/spinui_reloaded/SpinAttackRail.tga differ diff --git a/tools/audit_combat_ui.py b/tools/audit_combat_ui.py index aa97b9e..e3a55f0 100644 --- a/tools/audit_combat_ui.py +++ b/tools/audit_combat_ui.py @@ -13,7 +13,8 @@ # The effects-row grid is authored once in tools/restyle_combat.py; the audit # imports it so a geometry change can never pass by editing only one side. -from restyle_combat import (EFFECT_CHIP, EFFECT_ICON, EFFECT_NAME_WIDTH, +from restyle_combat import (ATTACK_PERIMETER_TEXTURE, ATTACK_RAIL_TEXTURE, + EFFECT_CHIP, EFFECT_ICON, EFFECT_NAME_WIDTH, EFFECT_NAME_X, EFFECT_PLATE_BLEED, EFFECT_ROW_WIDTH, EFFECT_TIMER_FONT, EFFECT_TIMER_HALF_WIDTH, EXTENDED_TARGET_COUNT, @@ -36,13 +37,15 @@ COMBAT_SKINS = (SKIN, GLASS_SKIN) STOCK = Path(r"C:\EQLegends\uifiles\default") -# Pin the client-native attack contract independently of the generator. This -# prevents a matching mistake in restyle_combat.py and paint_attack_indicator.py -# from approving itself. The source slices and RLE texture envelope mirror the -# known-working EQ Modern implementation; only destination thickness is SpinUI. +# Pin the client-native attack contract independently of the generator. The +# white RLE source maximizes the native red phase while EQ owns the cadence. ATTACK_TEXTURE_SIZE = (128, 32) -ATTACK_TEXTURE_COLOR = (189, 189, 189, 255) -ATTACK_EDGE_WIDTH = 8 +ATTACK_TEXTURE_COLOR = (255, 255, 255, 255) +ATTACK_PERIMETER_COLORS = { + (255, 255, 255, 255): 2 * 32 + (128 - 2), + (255, 255, 255, 0): 128 * 32 - (2 * 32 + (128 - 2)), +} +ATTACK_EDGE_WIDTH = 3 ATTACK_SOURCE_SIZES = { "A_AttackIndicator": (128, 32), "A_AttackIndicatorTop": (128, 2), @@ -52,10 +55,10 @@ "A_AttackIndicatorFill": (128, 32), } ATTACK_GEOMETRY = { - "A_AttackIndicatorAnimTop": (70, 78, 0, 0), - "A_AttackIndicatorAnimBottom": (10, 2, 0, 0), - "A_AttackIndicatorAnimLeft": (70, 2, 0, 8), - "A_AttackIndicatorAnimRight": (70, 2, 8, 0), + "A_AttackIndicatorAnimTop": (67, 70, 0, 0), + "A_AttackIndicatorAnimBottom": (9, 6, 0, 0), + "A_AttackIndicatorAnimLeft": (67, 6, 0, 3), + "A_AttackIndicatorAnimRight": (67, 6, 3, 0), } ATTACK_ANCHORS = { "A_AttackIndicatorAnimTop": ("true", "true", "true", "false"), @@ -194,29 +197,36 @@ def anchored_rect(node: ET.Element, width: int, def audit_attack_indicator_contract() -> None: """Prove every theme/variant keeps a visible native attack perimeter.""" for skin in COMBAT_SKINS: - texture_path = skin / "AttackIndicator.tga" - payload = texture_path.read_bytes() - if len(payload) < 18: - fail(f"{skin.name} AttackIndicator.tga has no complete TGA header") - width = int.from_bytes(payload[12:14], "little") - height = int.from_bytes(payload[14:16], "little") - if ((width, height) != ATTACK_TEXTURE_SIZE - or payload[2] != 10 - or payload[16] != 32 - or payload[17] & 0x0F != 8): - fail( - f"{skin.name} AttackIndicator.tga lost the proven 32-bit RLE " - f"envelope (type={payload[2]}, size={width}x{height}, " - f"depth={payload[16]}, descriptor={payload[17]})" - ) - with Image.open(texture_path) as source: - rgba = source.convert("RGBA") - colors = rgba.getcolors(maxcolors=width * height) - if colors != [(width * height, ATTACK_TEXTURE_COLOR)]: - fail( - f"{skin.name} AttackIndicator.tga is not the solid neutral " - "native tint source" - ) + for texture_name, expected_colors in ( + (ATTACK_RAIL_TEXTURE, + [(128 * 32, ATTACK_TEXTURE_COLOR)]), + (ATTACK_PERIMETER_TEXTURE, ATTACK_PERIMETER_COLORS)): + texture_path = skin / texture_name + payload = texture_path.read_bytes() + if len(payload) < 18: + fail(f"{skin.name}/{texture_name} has no complete TGA header") + width = int.from_bytes(payload[12:14], "little") + height = int.from_bytes(payload[14:16], "little") + if ((width, height) != ATTACK_TEXTURE_SIZE + or payload[2] != 10 + or payload[16] != 32 + or payload[17] & 0x0F != 8): + fail( + f"{skin.name}/{texture_name} lost the 32-bit RLE " + f"envelope (type={payload[2]}, size={width}x{height}, " + f"depth={payload[16]}, descriptor={payload[17]})" + ) + with Image.open(texture_path) as source: + colors = source.convert("RGBA").getcolors( + maxcolors=width * height) + if isinstance(expected_colors, dict): + actual_colors = { + color_value: count for count, color_value in (colors or []) + } + if actual_colors != expected_colors: + fail(f"{skin.name}/{texture_name} lost perimeter alpha") + elif colors != expected_colors: + fail(f"{skin.name}/{texture_name} lost opaque white rail") for filename in PLAYER_WINDOW_FILES: path = skin / filename @@ -227,15 +237,18 @@ def audit_attack_indicator_contract() -> None: animation = item(root, "Ui2DAnimation", name) frames = animation.findall("Frames") if len(frames) != 1: - fail(f"{label} {name} must have exactly one native frame") + fail(f"{label} {name} must have one native frame") frame = frames[0] + expected_texture = (ATTACK_PERIMETER_TEXTURE + if name == "A_AttackIndicatorFill" + else ATTACK_RAIL_TEXTURE) if (child_text(animation, "Cycle") != "false" - or child_text(frame, "Texture") != "AttackIndicator.tga" + or child_text(frame, "Texture") != expected_texture or dimensions_at(frame, "Size") != expected_size or (child_int(frame, "Location/X"), child_int(frame, "Location/Y")) != (0, 0) or frame.find("Duration") is not None): - fail(f"{label} {name} violates the native source contract") + fail(f"{label} {name} violates the native flash contract") edge_nodes: dict[str, ET.Element] = {} for name, expected_offsets in ATTACK_GEOMETRY.items(): @@ -259,14 +272,19 @@ def audit_attack_indicator_contract() -> None: or actual_anchors != ATTACK_ANCHORS[name]): fail( f"{label} {name} lost its native state binding or " - f"8px edge geometry: {actual_offsets} {actual_anchors}" + f"outer-seam geometry: {actual_offsets} {actual_anchors}" ) fill = item(root, "StaticAnimation", "A_AttackIndicatorAnimFill") if (child_text(fill, "ScreenID") != "A_AttackIndicatorAnimFill" - or fill.find("Animation") is not None - or fill.find("AutoDraw") is not None): - fail(f"{label} attack fill must remain an unbound placeholder") + or child_text(fill, "Animation") != "A_AttackIndicatorFill" + or child_text(fill, "AutoDraw") != "false" + or child_text(fill, "AutoStretch") != "true" + or tuple(child_int(fill, tag) for tag in ( + "TopAnchorOffset", "BottomAnchorOffset", + "LeftAnchorOffset", "RightAnchorOffset", + )) != (70, 6, 0, 0)): + fail(f"{label} attack fill lost its topmost perimeter binding") window = item(root, "Screen", "PlayerWindow") if dimensions(window) != (360, 193): @@ -276,21 +294,44 @@ def audit_attack_indicator_contract() -> None: ) if minimum != PLAYER_MIN_SIZE: fail(f"{label} changed the safe minimum size: {minimum}") - pieces = [(piece.text or "").strip() - for piece in window.findall("Pieces")] + root_pieces = [(piece.text or "").strip() + for piece in window.findall("Pieces")] expected_pieces = [ f"A_AttackIndicatorAnim{edge}" for edge in ("Top", "Bottom", "Left", "Right", "Fill") ] - player_subwindow_index = pieces.index("Screen:PlayerSubWindow") - compositor_slice = pieces[ - player_subwindow_index + 1:player_subwindow_index + 6 - ] - if (compositor_slice != expected_pieces - or any(pieces.count(piece) != 1 for piece in expected_pieces)): + subwindow = item(root, "Screen", "PlayerSubWindow") + subwindow_pieces = [(piece.text or "").strip() + for piece in subwindow.findall("Pieces")] + if (root_pieces[-5:] != expected_pieces + or any(root_pieces.count(piece) != 1 + for piece in expected_pieces) + or any(piece in subwindow_pieces + for piece in expected_pieces)): + fail( + f"{label} attack controls must be unique direct " + "PlayerWindow children in the final compositor slot" + ) + + subwindow_offsets = tuple(child_int(subwindow, tag) for tag in ( + "TopAnchorOffset", "BottomAnchorOffset", + "LeftAnchorOffset", "RightAnchorOffset", + )) + if subwindow_offsets != (70, 9, 3, 3): + fail( + f"{label} themed player frame can cover the native " + f"attack perimeter: {subwindow_offsets}" + ) + + buff_window = item(root, "Screen", "PW_BuffWindow") + buff_offsets = ( + child_int(buff_window, "TopAnchorOffset"), + child_int(buff_window, "BottomAnchorOffset"), + ) + if buff_offsets != (0, 70): fail( - f"{label} attack rails must be unique and immediately " - "follow PlayerSubWindow in the native compositor slot" + f"{label} buff strip can overlap the top attack rail: " + f"{buff_offsets}" ) combat_state = item(root, "Button", "PW_CombatStateAnim") @@ -304,15 +345,15 @@ def audit_attack_indicator_contract() -> None: frame_width, frame_height = size expected_rects = { "A_AttackIndicatorAnimTop": - (0, 70, frame_width, 70 + ATTACK_EDGE_WIDTH), + (0, 67, frame_width, 70), "A_AttackIndicatorAnimBottom": - (0, frame_height - 2 - ATTACK_EDGE_WIDTH, - frame_width, frame_height - 2), + (0, frame_height - 9, frame_width, + frame_height - 6), "A_AttackIndicatorAnimLeft": - (0, 70, ATTACK_EDGE_WIDTH, frame_height - 2), + (0, 67, 3, frame_height - 6), "A_AttackIndicatorAnimRight": - (frame_width - ATTACK_EDGE_WIDTH, 70, - frame_width, frame_height - 2), + (frame_width - 3, 67, frame_width, + frame_height - 6), } actual_rects = { name: anchored_rect(node, frame_width, frame_height) @@ -355,6 +396,15 @@ def audit_player_and_target() -> None: for path in shared_progression_paths: if child_text(exp, path) != child_text(alt_adv, path): fail(f"player EXP and AA horizontal rendering differs at {path}") + exp_height = (child_int(exp, "BottomAnchorOffset") + - child_int(exp, "TopAnchorOffset")) + aa_height = (child_int(alt_adv, "BottomAnchorOffset") + - child_int(alt_adv, "TopAnchorOffset")) + if (exp_height, aa_height) != (10, 11): + fail( + "player AA gauge must retain its one-pixel fractional-scale " + f"weight correction: EXP {exp_height}, AA {aa_height}" + ) require_binding(player, "Gauge", "PW_Castspell_Gauge", "PW_Castspell_Gauge", 7) require_binding(player, "Label", "Player_ManaLabel", "ManaLabel", 1009) require_binding(player, "Label", "PW_MPNumbers", "PW_MPNumbers", 128) @@ -1286,7 +1336,7 @@ def main() -> int: print("Combat Command Center audit: ALL PASS") print(" Player/Target/ToT | Group 1..11 | XTarget 0..22 | Raid groups 1..12") print(" buffs 30 | songs 15 | spell gems 14 | hotbars 11 x 12 | stance + invocation") - print(" 52 compatibility aliases + 14 native 8px attack perimeters + spell ledger") + print(" 52 compatibility aliases + 14 foreground native attack perimeters + spell ledger") print(" contrast AAA/AA | July stock parity " + ("PASS" if stock_checked else "not available")) return 0 diff --git a/tools/paint_attack_indicator.py b/tools/paint_attack_indicator.py index 43624a2..de28b29 100644 --- a/tools/paint_attack_indicator.py +++ b/tools/paint_attack_indicator.py @@ -1,13 +1,11 @@ #!/usr/bin/env python3 """Paint SpinUI's client-native auto-attack indicator texture. -EverQuest owns the visibility and flashing cadence of the four -``A_AttackIndicatorAnim*`` edge widgets in ``EQUI_PlayerWindow.xml``. The -texture therefore remains a single, fully opaque neutral frame like the -working Modern UI contract. EverQuest modulates that neutral source from -white/gray to red; pre-coloring the source red destroys the visible flash -because every tint phase remains red. Only narrow edge slices are drawn by -the XML, and the fill widget is intentionally unbound. +EverQuest owns the visibility and flashing cadence of the five recognized +``A_AttackIndicatorAnim*`` widgets in ``EQUI_PlayerWindow.xml``. Narrow edge +slices use a fully opaque neutral-white rail; the required Fill widget uses a +transparent-center white perimeter. The client owns attack-on visibility, +red tint, and flashing cadence. Run from the repository root: python tools/paint_attack_indicator.py @@ -15,32 +13,45 @@ from pathlib import Path -from PIL import Image +from PIL import Image, ImageDraw REPO = Path(__file__).resolve().parent.parent OUTPUT = REPO / "spinui_reloaded" / "AttackIndicator.tga" +RAIL_OUTPUT = REPO / "spinui_reloaded" / "SpinAttackRail.tga" +PERIMETER_OUTPUT = REPO / "spinui_reloaded" / "SpinAttackPerimeter.tga" FRAME_SIZE = (128, 32) SIZE = FRAME_SIZE -EDGE_WIDTH = 8 -# Use the exact neutral source proven by EQ's working Modern UI. The client -# modulates this gray to its bright neutral/red phases only while attack is on. -# Pre-coloring it red collapses the modulation, and an uncompressed 32-bit -# pure-white texture is an unproven legacy-renderer combination. -COLOR = (189, 189, 189, 255) +EDGE_WIDTH = 3 +COLOR = (255, 255, 255, 255) def render() -> Image.Image: return Image.new("RGBA", SIZE, COLOR) +def render_perimeter() -> Image.Image: + image = Image.new("RGBA", SIZE, (255, 255, 255, 0)) + draw = ImageDraw.Draw(image) + draw.line((0, 0, 0, SIZE[1] - 1), fill=COLOR, width=1) + draw.line((SIZE[0] - 1, 0, SIZE[0] - 1, SIZE[1] - 1), + fill=COLOR, width=1) + draw.line((0, SIZE[1] - 1, SIZE[0] - 1, SIZE[1] - 1), + fill=COLOR, width=1) + return image + + def main() -> int: OUTPUT.parent.mkdir(parents=True, exist_ok=True) - # RLE 32-bit is the known-working Modern TGA envelope. For this solid - # source Pillow emits a deterministic 204-byte file (type 10, alpha 8). - render().save(OUTPUT, format="TGA", compression="tga_rle") + # Retain the proven 32-bit RLE envelope used by the legacy renderer. + rail = render() + rail.save(OUTPUT, format="TGA", compression="tga_rle") + rail.save(RAIL_OUTPUT, format="TGA", compression="tga_rle") + render_perimeter().save( + PERIMETER_OUTPUT, format="TGA", compression="tga_rle", + ) print( - "AttackIndicator.tga: native neutral RLE attack rail painted | " - "8px EverQuest tint-driven pulse | no fill wash" + "Spin attack textures: native white rail + transparent-center " + "foreground perimeter | client red flash | no fill wash" ) return 0 diff --git a/tools/restyle_combat.py b/tools/restyle_combat.py index 286124f..631a0f2 100644 --- a/tools/restyle_combat.py +++ b/tools/restyle_combat.py @@ -62,16 +62,18 @@ # The authored 360x193 command frames remain unchanged on first load. Their # anchored subwindows can safely contract to these Legends-compatible bounds, # so users may resize them without clipping the command rows. -PLAYER_MIN_SIZE = (280, 174) +PLAYER_MIN_SIZE = (280, 193) TARGET_MIN_SIZE = (260, 174) # EverQuest recognizes the attack indicator by exact animation and ScreenID -# names, then owns its attack-on visibility and pulse. Keep the proven stock -# source slices byte-for-byte compatible and make only the destination rails -# bolder. Eight screen pixels remains clear at high resolutions without -# washing over the command-frame contents. -ATTACK_EDGE_WIDTH = 8 +# names, then owns its attack-on visibility, red tint, and flashing cadence. +# Preserve that native one-frame contract. The rail occupies a crisp exposed +# outer seam around the compact subwindow, where opaque themed chrome cannot +# bury it. Three pixels stays resolution-visible without becoming a fill. +ATTACK_EDGE_WIDTH = 3 ATTACK_FRAME_SIZE = (128, 32) ATTACK_TEXTURE_SIZE = ATTACK_FRAME_SIZE +ATTACK_RAIL_TEXTURE = "SpinAttackRail.tga" +ATTACK_PERIMETER_TEXTURE = "SpinAttackPerimeter.tga" ATTACK_SOURCE_SIZES = { "A_AttackIndicator": ATTACK_FRAME_SIZE, "A_AttackIndicatorTop": (128, 2), @@ -465,6 +467,17 @@ def style_player() -> None: text, "Gauge", name, show_total_progression_ticks, ) + + def match_aa_gauge_weight(block: str) -> str: + # At fractional UI scales the lower AA row loses one rasterized pixel + # even with the same authored height as EXP. Give its top edge one + # extra source pixel so both progression tracks read at equal weight + # in game, without changing the horizontal percentage calculation. + return set_value(block, "TopAnchorOffset", 83) + + text = change_item( + text, "Gauge", "PW_AltAdvGauge", match_aa_gauge_weight, + ) for name, color in (("PW_Level", GOLD_BRIGHT), ("PW_Class", TEXT), ("PW_StanceLabel", GOLD_BRIGHT), ("PW_InvocationInfo", CYAN)): @@ -499,16 +512,47 @@ def invocation_rail(block: str) -> str: text = change_item(text, "Label", "PW_StanceLabel", stance_rail) text = change_item(text, "Label", "PW_InvocationInfo", invocation_rail) + # Preserve the stock declaration for schema parity, while animations use + # Spin-specific filenames that cannot collide with an already cached skin. + stock_info = item_pattern("TextureInfo", "AttackIndicator.tga").search(text) + rail_info = item_pattern("TextureInfo", ATTACK_RAIL_TEXTURE).search(text) + if stock_info is None and rail_info is not None: + compatibility_info = rail_info.group(0).replace( + ATTACK_RAIL_TEXTURE, "AttackIndicator.tga", 1, + ) + text = (text[:rail_info.start()] + compatibility_info + "\n\n\t" + + text[rail_info.start():]) + stock_info = item_pattern( + "TextureInfo", "AttackIndicator.tga" + ).search(text) + if stock_info is None: + fail("stock attack TextureInfo disappeared") + if f'' not in text: + rail_block = stock_info.group(0).replace( + "AttackIndicator.tga", ATTACK_RAIL_TEXTURE, 1, + ) + text = (text[:stock_info.end()] + "\n\n\t" + rail_block + + text[stock_info.end():]) text = change_item( - text, "TextureInfo", "AttackIndicator.tga", + text, "TextureInfo", ATTACK_RAIL_TEXTURE, lambda b: set_container( b, "Size", CX=ATTACK_TEXTURE_SIZE[0], CY=ATTACK_TEXTURE_SIZE[1]), ) - - def attack_animation(block: str, size: tuple[int, int]) -> str: - # Match the working native source contract exactly: EverQuest toggles, - # tints, and flashes these stock-named widgets itself. A custom Cycle - # can look correct in static audits while never advancing in game. + rail_info = item_pattern("TextureInfo", ATTACK_RAIL_TEXTURE).search(text) + if rail_info is None: + fail("attack rail TextureInfo disappeared") + if f'' not in text: + perimeter_info = rail_info.group(0).replace( + ATTACK_RAIL_TEXTURE, ATTACK_PERIMETER_TEXTURE, 1, + ) + text = (text[:rail_info.end()] + "\n\n\t" + perimeter_info + + text[rail_info.end():]) + + def attack_animation(block: str, size: tuple[int, int], + texture: str) -> str: + # Match the client-owned attack state contract exactly. Custom cycles + # do not advance reliably while these AutoDraw=false widgets are under + # state control and suppress the game's native flash. block = set_value(block, "Cycle", "false") block = re.sub(r"\n\t\t.*?", "", block, flags=re.DOTALL) @@ -519,7 +563,7 @@ def attack_animation(block: str, size: tuple[int, int]) -> str: cycle_end += len(cycle_marker) frames = ( "\n\t\t\n" - "\t\t\tAttackIndicator.tga\n" + f"\t\t\t{texture}\n" "\t\t\t\n" "\t\t\t\t0\n" "\t\t\t\t0\n" @@ -533,16 +577,24 @@ def attack_animation(block: str, size: tuple[int, int]) -> str: return block[:cycle_end] + frames + block[cycle_end:] for name, size in ATTACK_SOURCE_SIZES.items(): + texture = (ATTACK_PERIMETER_TEXTURE + if name == "A_AttackIndicatorFill" + else ATTACK_RAIL_TEXTURE) text = change_item( text, "Ui2DAnimation", name, - lambda b, s=size: attack_animation(b, s), + lambda b, s=size, t=texture: attack_animation(b, s, t), ) + # Root-relative seam around PlayerSubWindow (root y=70..184). The client + # requires these recognized controls as direct PlayerWindow children. attack_edges = { - "A_AttackIndicatorAnimTop": (70, 70 + ATTACK_EDGE_WIDTH, 0, 0), - "A_AttackIndicatorAnimBottom": (2 + ATTACK_EDGE_WIDTH, 2, 0, 0), - "A_AttackIndicatorAnimLeft": (70, 2, 0, ATTACK_EDGE_WIDTH), - "A_AttackIndicatorAnimRight": (70, 2, ATTACK_EDGE_WIDTH, 0), + # Sit immediately above the subwindow so this foreground rail cannot + # cross PAL/MNK/ENC. Side rails overlap the top corners so the pulse is + # one continuous square rather than four disconnected strokes. + "A_AttackIndicatorAnimTop": (67, 70, 0, 0), + "A_AttackIndicatorAnimBottom": (9, 6, 0, 0), + "A_AttackIndicatorAnimLeft": (67, 6, 0, 3), + "A_AttackIndicatorAnimRight": (67, 6, 3, 0), } for name, offsets in attack_edges.items(): def attack_edge(block: str, values=offsets) -> str: @@ -556,11 +608,24 @@ def attack_edge(block: str, values=offsets) -> str: text = change_item(text, "StaticAnimation", name, attack_edge) def attack_fill(_block: str) -> str: - # Keep the stock symbol because the client expects it, but leave it - # deliberately unbound. Only the four edge widgets may be drawn. + # The client requires this direct child. Its transparent-center art + # creates a final topmost perimeter without covering any HUD content. return ( '\n' '\t\tA_AttackIndicatorAnimFill\n' + '\t\tA_AttackIndicatorFill\n' + '\t\ttrue\n' + '\t\tfalse\n' + '\t\ttrue\n' + '\t\ttrue\n' + '\t\t70\n' + '\t\t6\n' + '\t\t0\n' + '\t\t0\n' + '\t\ttrue\n' + '\t\tfalse\n' + '\t\ttrue\n' + '\t\tfalse\n' '\t' ) @@ -584,6 +649,45 @@ def attack_fill(_block: str) -> str: text, ) + attack_pieces = tuple( + f"A_AttackIndicatorAnim{edge}" + for edge in ("Top", "Bottom", "Left", "Right", "Fill") + ) + + def clean_subwindow(block: str) -> str: + for piece in attack_pieces: + block = re.sub( + rf"\n[ \t]*{piece}", "", block, + ) + return block + + def expose_attack_perimeter(block: str) -> str: + block = clean_subwindow(block) + # The themed subwindow previously overhung the root by one pixel and + # ended on the bottom rail. A three-pixel horizontal inset leaves the + # native attack pulse physically exposed; the nine-pixel bottom inset + # removes the unused strip beneath stance/invocation while keeping the + # authored 360x193 interaction and buff canvas unchanged. + block = set_value(block, "TopAnchorOffset", "70") + block = set_value(block, "BottomAnchorOffset", "9") + block = set_value(block, "LeftAnchorOffset", "3") + block = set_value(block, "RightAnchorOffset", "3") + return block + + text = change_item( + text, "Screen", "PlayerSubWindow", expose_attack_perimeter, + ) + + def lift_buff_strip(block: str) -> str: + # Preserve the 70px host height and full buff capacity; translate it + # upward by two pixels so bottom-anchored 23px icons cannot touch the + # flashing top seam. + block = set_value(block, "TopAnchorOffset", "0") + block = set_value(block, "BottomAnchorOffset", "70") + return block + + text = change_item(text, "Screen", "PW_BuffWindow", lift_buff_strip) + def root_style(block: str) -> str: block = set_container(block, "Size", CX=360, CY=193) block = set_or_add_value( @@ -598,16 +702,9 @@ def root_style(block: str) -> str: # live client; the compact PlayerSubWindow remains the visible frame. block = set_value(block, "Style_Border", "false") block = set_value(block, "Style_Sizable", "true") - # Keep the native rails in EverQuest's proven compositor slot: directly - # after PlayerSubWindow. SIDL Pieces do not behave like a browser DOM; - # placing these state-owned children last causes the themed subwindow - # chrome to be composed over parts of the pulse in the live client. - # Default Modern uses this exact placement and renders the rails above - # the command frame, which is the behavior Reloaded and Glass need. - attack_pieces = tuple( - f"A_AttackIndicatorAnim{edge}" - for edge in ("Top", "Bottom", "Left", "Right", "Fill") - ) + # These exact controls must be direct children of PlayerWindow: the + # client resolves them by name when attack state changes. Keep them as + # the final children so no root chrome or interaction surface follows. for piece in attack_pieces: block = re.sub( rf"\n[ \t]*{piece}", "", block, @@ -616,13 +713,10 @@ def root_style(block: str) -> str: f"\n\t\t{piece}" for piece in attack_pieces ) block, count = re.subn( - r"(\n[ \t]*Screen:PlayerSubWindow)", - r"\1" + pieces, - block, - count=1, + r"\n\t$", pieces + "\n\t", block, count=1, ) if count != 1: - fail("PlayerWindow root has no PlayerSubWindow piece") + fail("PlayerWindow root has no closing Screen tag") return block text = change_item(text, "Screen", "PlayerWindow", root_style)