From 5f642308185182850546e013ec1649c36ade6f4e Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:20:23 +0200 Subject: [PATCH 01/11] Deduplicate player theme assets --- game/themes/Deluxe/Blue.ini | 48 +++++++++--------- game/themes/Deluxe/Fall.ini | 48 +++++++++--------- game/themes/Deluxe/Ocean.ini | 48 +++++++++--------- game/themes/Deluxe/Ribbon.ini | 48 +++++++++--------- game/themes/Deluxe/Summer.ini | 48 +++++++++--------- game/themes/Deluxe/Winter.ini | 48 +++++++++--------- game/themes/Deluxe/[name]noavatar.png | Bin 0 -> 2550 bytes game/themes/Deluxe/[name]noavatarP1.png | Bin 985 -> 0 bytes game/themes/Deluxe/[name]noavatarP2.png | Bin 1019 -> 0 bytes game/themes/Deluxe/[name]noavatarP3.png | Bin 1040 -> 0 bytes game/themes/Deluxe/[name]noavatarP4.png | Bin 1015 -> 0 bytes game/themes/Deluxe/[name]noavatarP5.png | Bin 1016 -> 0 bytes game/themes/Deluxe/[name]noavatarP6.png | Bin 1039 -> 0 bytes .../Deluxe/[sing.player1]lyric_active.png | Bin 884 -> 0 bytes .../Deluxe/[sing.player1]lyric_inactive.png | Bin 862 -> 0 bytes .../Deluxe/[sing.player2]lyric_active.png | Bin 1034 -> 0 bytes .../Deluxe/[sing.player2]lyric_inactive.png | Bin 948 -> 0 bytes .../Deluxe/[sing.player3]lyric_active.png | Bin 988 -> 0 bytes .../Deluxe/[sing.player3]lyric_inactive.png | Bin 911 -> 0 bytes .../Deluxe/[sing.player4]lyric_active.png | Bin 966 -> 0 bytes .../Deluxe/[sing.player4]lyric_inactive.png | Bin 861 -> 0 bytes .../Deluxe/[sing.player5]lyric_active.png | Bin 959 -> 0 bytes .../Deluxe/[sing.player5]lyric_inactive.png | Bin 877 -> 0 bytes .../Deluxe/[sing.player6]lyric_active.png | Bin 960 -> 0 bytes .../Deluxe/[sing.player6]lyric_inactive.png | Bin 872 -> 0 bytes .../Deluxe/[sing.player]lyric_active.png | Bin 0 -> 1926 bytes .../Deluxe/[sing.player]lyric_inactive.png | Bin 0 -> 1865 bytes game/themes/Modern/Blue.ini | 48 +++++++++--------- game/themes/Modern/Winter.ini | 48 +++++++++--------- game/themes/Modern/[name]noavatar.png | Bin 0 -> 2550 bytes game/themes/Modern/[name]noavatarP1.png | Bin 985 -> 0 bytes game/themes/Modern/[name]noavatarP10.png | Bin 973 -> 0 bytes game/themes/Modern/[name]noavatarP11.png | Bin 973 -> 0 bytes game/themes/Modern/[name]noavatarP12.png | Bin 973 -> 0 bytes game/themes/Modern/[name]noavatarP2.png | Bin 1019 -> 0 bytes game/themes/Modern/[name]noavatarP3.png | Bin 1040 -> 0 bytes game/themes/Modern/[name]noavatarP4.png | Bin 1015 -> 0 bytes game/themes/Modern/[name]noavatarP5.png | Bin 1016 -> 0 bytes game/themes/Modern/[name]noavatarP6.png | Bin 1039 -> 0 bytes game/themes/Modern/[name]noavatarP7.png | Bin 973 -> 0 bytes game/themes/Modern/[name]noavatarP8.png | Bin 973 -> 0 bytes game/themes/Modern/[name]noavatarP9.png | Bin 973 -> 0 bytes .../Modern/[sing.player1]lyric_active.png | Bin 884 -> 0 bytes .../Modern/[sing.player1]lyric_inactive.png | Bin 862 -> 0 bytes .../Modern/[sing.player2]lyric_active.png | Bin 1034 -> 0 bytes .../Modern/[sing.player2]lyric_inactive.png | Bin 948 -> 0 bytes .../Modern/[sing.player3]lyric_active.png | Bin 988 -> 0 bytes .../Modern/[sing.player3]lyric_inactive.png | Bin 911 -> 0 bytes .../Modern/[sing.player4]lyric_active.png | Bin 966 -> 0 bytes .../Modern/[sing.player4]lyric_inactive.png | Bin 861 -> 0 bytes .../Modern/[sing.player5]lyric_active.png | Bin 959 -> 0 bytes .../Modern/[sing.player5]lyric_inactive.png | Bin 877 -> 0 bytes .../Modern/[sing.player6]lyric_active.png | Bin 960 -> 0 bytes .../Modern/[sing.player6]lyric_inactive.png | Bin 872 -> 0 bytes .../Modern/[sing.player]lyric_active.png | Bin 0 -> 1926 bytes .../Modern/[sing.player]lyric_inactive.png | Bin 0 -> 1865 bytes 56 files changed, 192 insertions(+), 192 deletions(-) create mode 100644 game/themes/Deluxe/[name]noavatar.png delete mode 100644 game/themes/Deluxe/[name]noavatarP1.png delete mode 100644 game/themes/Deluxe/[name]noavatarP2.png delete mode 100644 game/themes/Deluxe/[name]noavatarP3.png delete mode 100644 game/themes/Deluxe/[name]noavatarP4.png delete mode 100644 game/themes/Deluxe/[name]noavatarP5.png delete mode 100644 game/themes/Deluxe/[name]noavatarP6.png delete mode 100644 game/themes/Deluxe/[sing.player1]lyric_active.png delete mode 100644 game/themes/Deluxe/[sing.player1]lyric_inactive.png delete mode 100644 game/themes/Deluxe/[sing.player2]lyric_active.png delete mode 100644 game/themes/Deluxe/[sing.player2]lyric_inactive.png delete mode 100644 game/themes/Deluxe/[sing.player3]lyric_active.png delete mode 100644 game/themes/Deluxe/[sing.player3]lyric_inactive.png delete mode 100644 game/themes/Deluxe/[sing.player4]lyric_active.png delete mode 100644 game/themes/Deluxe/[sing.player4]lyric_inactive.png delete mode 100644 game/themes/Deluxe/[sing.player5]lyric_active.png delete mode 100644 game/themes/Deluxe/[sing.player5]lyric_inactive.png delete mode 100644 game/themes/Deluxe/[sing.player6]lyric_active.png delete mode 100644 game/themes/Deluxe/[sing.player6]lyric_inactive.png create mode 100644 game/themes/Deluxe/[sing.player]lyric_active.png create mode 100644 game/themes/Deluxe/[sing.player]lyric_inactive.png create mode 100644 game/themes/Modern/[name]noavatar.png delete mode 100644 game/themes/Modern/[name]noavatarP1.png delete mode 100644 game/themes/Modern/[name]noavatarP10.png delete mode 100644 game/themes/Modern/[name]noavatarP11.png delete mode 100644 game/themes/Modern/[name]noavatarP12.png delete mode 100644 game/themes/Modern/[name]noavatarP2.png delete mode 100644 game/themes/Modern/[name]noavatarP3.png delete mode 100644 game/themes/Modern/[name]noavatarP4.png delete mode 100644 game/themes/Modern/[name]noavatarP5.png delete mode 100644 game/themes/Modern/[name]noavatarP6.png delete mode 100644 game/themes/Modern/[name]noavatarP7.png delete mode 100644 game/themes/Modern/[name]noavatarP8.png delete mode 100644 game/themes/Modern/[name]noavatarP9.png delete mode 100644 game/themes/Modern/[sing.player1]lyric_active.png delete mode 100644 game/themes/Modern/[sing.player1]lyric_inactive.png delete mode 100644 game/themes/Modern/[sing.player2]lyric_active.png delete mode 100644 game/themes/Modern/[sing.player2]lyric_inactive.png delete mode 100644 game/themes/Modern/[sing.player3]lyric_active.png delete mode 100644 game/themes/Modern/[sing.player3]lyric_inactive.png delete mode 100644 game/themes/Modern/[sing.player4]lyric_active.png delete mode 100644 game/themes/Modern/[sing.player4]lyric_inactive.png delete mode 100644 game/themes/Modern/[sing.player5]lyric_active.png delete mode 100644 game/themes/Modern/[sing.player5]lyric_inactive.png delete mode 100644 game/themes/Modern/[sing.player6]lyric_active.png delete mode 100644 game/themes/Modern/[sing.player6]lyric_inactive.png create mode 100644 game/themes/Modern/[sing.player]lyric_active.png create mode 100644 game/themes/Modern/[sing.player]lyric_inactive.png diff --git a/game/themes/Deluxe/Blue.ini b/game/themes/Deluxe/Blue.ini index c545578d6..08eb10c27 100644 --- a/game/themes/Deluxe/Blue.ini +++ b/game/themes/Deluxe/Blue.ini @@ -137,12 +137,12 @@ ScoreEndCap = [score]endcap.png ScoreLine = [score]line.png PlayerNumberBox = [main]playerNumberBox.png -PlayerIDBox01 = [sing.player1]lyric_active.png -PlayerIDBox02 = [sing.player2]lyric_active.png -PlayerIDBox03 = [sing.player3]lyric_active.png -PlayerIDBox04 = [sing.player4]lyric_active.png -PlayerIDBox05 = [sing.player5]lyric_active.png -PlayerIDBox06 = [sing.player6]lyric_active.png +PlayerIDBox01 = [sing.player]lyric_active.png +PlayerIDBox02 = [sing.player]lyric_active.png +PlayerIDBox03 = [sing.player]lyric_active.png +PlayerIDBox04 = [sing.player]lyric_active.png +PlayerIDBox05 = [sing.player]lyric_active.png +PlayerIDBox06 = [sing.player]lyric_active.png # these icons are part of the tango icon set # licensed under Creative Commons Attribution Share-Alike license @@ -251,18 +251,18 @@ BGFade = [special]bg_fade.png # # # D U E T # # # -LyricIcon_P1 = [sing.player1]lyric_active.png -LyricIconD_P1 = [sing.player1]lyric_inactive.png -LyricIcon_P2 = [sing.player2]lyric_active.png -LyricIconD_P2 = [sing.player2]lyric_inactive.png -LyricIcon_P3 = [sing.player3]lyric_active.png -LyricIconD_P3 = [sing.player3]lyric_inactive.png -LyricIcon_P4 = [sing.player4]lyric_active.png -LyricIconD_P4 = [sing.player4]lyric_inactive.png -LyricIcon_P5 = [sing.player5]lyric_active.png -LyricIconD_P5 = [sing.player5]lyric_inactive.png -LyricIcon_P6 = [sing.player6]lyric_active.png -LyricIconD_P6 = [sing.player6]lyric_inactive.png +LyricIcon_P1 = [sing.player]lyric_active.png +LyricIconD_P1 = [sing.player]lyric_inactive.png +LyricIcon_P2 = [sing.player]lyric_active.png +LyricIconD_P2 = [sing.player]lyric_inactive.png +LyricIcon_P3 = [sing.player]lyric_active.png +LyricIconD_P3 = [sing.player]lyric_inactive.png +LyricIcon_P4 = [sing.player]lyric_active.png +LyricIconD_P4 = [sing.player]lyric_inactive.png +LyricIcon_P5 = [sing.player]lyric_active.png +LyricIconD_P5 = [sing.player]lyric_inactive.png +LyricIcon_P6 = [sing.player]lyric_active.png +LyricIconD_P6 = [sing.player]lyric_inactive.png # # # DOWNLOAD SCORE # # # @@ -316,9 +316,9 @@ SelectFrame = [name]select.png AvatarFrame2 = [score]frame.png # # # NO AVATARS # # # -NoAvatar_P1 = [name]noavatarP1.png -NoAvatar_P2 = [name]noavatarP2.png -NoAvatar_P3 = [name]noavatarP3.png -NoAvatar_P4 = [name]noavatarP4.png -NoAvatar_P5 = [name]noavatarP5.png -NoAvatar_P6 = [name]noavatarP6.png +NoAvatar_P1 = [name]noavatar.png +NoAvatar_P2 = [name]noavatar.png +NoAvatar_P3 = [name]noavatar.png +NoAvatar_P4 = [name]noavatar.png +NoAvatar_P5 = [name]noavatar.png +NoAvatar_P6 = [name]noavatar.png diff --git a/game/themes/Deluxe/Fall.ini b/game/themes/Deluxe/Fall.ini index 909f0f609..5fd2b779a 100644 --- a/game/themes/Deluxe/Fall.ini +++ b/game/themes/Deluxe/Fall.ini @@ -137,12 +137,12 @@ ScoreEndCap = [score]endcap.png ScoreLine = [score]line.png PlayerNumberBox = [main]playerNumberBox.png -PlayerIDBox01 = [sing.player1]lyric_active.png -PlayerIDBox02 = [sing.player2]lyric_active.png -PlayerIDBox03 = [sing.player3]lyric_active.png -PlayerIDBox04 = [sing.player4]lyric_active.png -PlayerIDBox05 = [sing.player5]lyric_active.png -PlayerIDBox06 = [sing.player6]lyric_active.png +PlayerIDBox01 = [sing.player]lyric_active.png +PlayerIDBox02 = [sing.player]lyric_active.png +PlayerIDBox03 = [sing.player]lyric_active.png +PlayerIDBox04 = [sing.player]lyric_active.png +PlayerIDBox05 = [sing.player]lyric_active.png +PlayerIDBox06 = [sing.player]lyric_active.png # these icons are part of the tango icon set # licensed under Creative Commons Attribution Share-Alike license @@ -251,18 +251,18 @@ BGFade = [special]bg_fade.png # # # D U E T # # # -LyricIcon_P1 = [sing.player1]lyric_active.png -LyricIconD_P1 = [sing.player1]lyric_inactive.png -LyricIcon_P2 = [sing.player2]lyric_active.png -LyricIconD_P2 = [sing.player2]lyric_inactive.png -LyricIcon_P3 = [sing.player3]lyric_active.png -LyricIconD_P3 = [sing.player3]lyric_inactive.png -LyricIcon_P4 = [sing.player4]lyric_active.png -LyricIconD_P4 = [sing.player4]lyric_inactive.png -LyricIcon_P5 = [sing.player5]lyric_active.png -LyricIconD_P5 = [sing.player5]lyric_inactive.png -LyricIcon_P6 = [sing.player6]lyric_active.png -LyricIconD_P6 = [sing.player6]lyric_inactive.png +LyricIcon_P1 = [sing.player]lyric_active.png +LyricIconD_P1 = [sing.player]lyric_inactive.png +LyricIcon_P2 = [sing.player]lyric_active.png +LyricIconD_P2 = [sing.player]lyric_inactive.png +LyricIcon_P3 = [sing.player]lyric_active.png +LyricIconD_P3 = [sing.player]lyric_inactive.png +LyricIcon_P4 = [sing.player]lyric_active.png +LyricIconD_P4 = [sing.player]lyric_inactive.png +LyricIcon_P5 = [sing.player]lyric_active.png +LyricIconD_P5 = [sing.player]lyric_inactive.png +LyricIcon_P6 = [sing.player]lyric_active.png +LyricIconD_P6 = [sing.player]lyric_inactive.png # # # DOWNLOAD SCORE # # # @@ -316,9 +316,9 @@ SelectFrame = [name]select.png AvatarFrame2 = [score]frame.png # # # NO AVATARS # # # -NoAvatar_P1 = [name]noavatarP1.png -NoAvatar_P2 = [name]noavatarP2.png -NoAvatar_P3 = [name]noavatarP3.png -NoAvatar_P4 = [name]noavatarP4.png -NoAvatar_P5 = [name]noavatarP5.png -NoAvatar_P6 = [name]noavatarP6.png +NoAvatar_P1 = [name]noavatar.png +NoAvatar_P2 = [name]noavatar.png +NoAvatar_P3 = [name]noavatar.png +NoAvatar_P4 = [name]noavatar.png +NoAvatar_P5 = [name]noavatar.png +NoAvatar_P6 = [name]noavatar.png diff --git a/game/themes/Deluxe/Ocean.ini b/game/themes/Deluxe/Ocean.ini index 1c35f72cf..ecae95978 100644 --- a/game/themes/Deluxe/Ocean.ini +++ b/game/themes/Deluxe/Ocean.ini @@ -137,12 +137,12 @@ ScoreEndCap = [score]endcap.png ScoreLine = [score]line.png PlayerNumberBox = [main]playerNumberBox.png -PlayerIDBox01 = [sing.player1]lyric_active.png -PlayerIDBox02 = [sing.player2]lyric_active.png -PlayerIDBox03 = [sing.player3]lyric_active.png -PlayerIDBox04 = [sing.player4]lyric_active.png -PlayerIDBox05 = [sing.player5]lyric_active.png -PlayerIDBox06 = [sing.player6]lyric_active.png +PlayerIDBox01 = [sing.player]lyric_active.png +PlayerIDBox02 = [sing.player]lyric_active.png +PlayerIDBox03 = [sing.player]lyric_active.png +PlayerIDBox04 = [sing.player]lyric_active.png +PlayerIDBox05 = [sing.player]lyric_active.png +PlayerIDBox06 = [sing.player]lyric_active.png # these icons are part of the tango icon set # licensed under Creative Commons Attribution Share-Alike license @@ -251,18 +251,18 @@ BGFade = [special]bg_fade.png # # # D U E T # # # -LyricIcon_P1 = [sing.player1]lyric_active.png -LyricIconD_P1 = [sing.player1]lyric_inactive.png -LyricIcon_P2 = [sing.player2]lyric_active.png -LyricIconD_P2 = [sing.player2]lyric_inactive.png -LyricIcon_P3 = [sing.player3]lyric_active.png -LyricIconD_P3 = [sing.player3]lyric_inactive.png -LyricIcon_P4 = [sing.player4]lyric_active.png -LyricIconD_P4 = [sing.player4]lyric_inactive.png -LyricIcon_P5 = [sing.player5]lyric_active.png -LyricIconD_P5 = [sing.player5]lyric_inactive.png -LyricIcon_P6 = [sing.player6]lyric_active.png -LyricIconD_P6 = [sing.player6]lyric_inactive.png +LyricIcon_P1 = [sing.player]lyric_active.png +LyricIconD_P1 = [sing.player]lyric_inactive.png +LyricIcon_P2 = [sing.player]lyric_active.png +LyricIconD_P2 = [sing.player]lyric_inactive.png +LyricIcon_P3 = [sing.player]lyric_active.png +LyricIconD_P3 = [sing.player]lyric_inactive.png +LyricIcon_P4 = [sing.player]lyric_active.png +LyricIconD_P4 = [sing.player]lyric_inactive.png +LyricIcon_P5 = [sing.player]lyric_active.png +LyricIconD_P5 = [sing.player]lyric_inactive.png +LyricIcon_P6 = [sing.player]lyric_active.png +LyricIconD_P6 = [sing.player]lyric_inactive.png # # # DOWNLOAD SCORE # # # @@ -316,9 +316,9 @@ SelectFrame = [name]select.png AvatarFrame2 = [score]frame.png # # # NO AVATARS # # # -NoAvatar_P1 = [name]noavatarP1.png -NoAvatar_P2 = [name]noavatarP2.png -NoAvatar_P3 = [name]noavatarP3.png -NoAvatar_P4 = [name]noavatarP4.png -NoAvatar_P5 = [name]noavatarP5.png -NoAvatar_P6 = [name]noavatarP6.png +NoAvatar_P1 = [name]noavatar.png +NoAvatar_P2 = [name]noavatar.png +NoAvatar_P3 = [name]noavatar.png +NoAvatar_P4 = [name]noavatar.png +NoAvatar_P5 = [name]noavatar.png +NoAvatar_P6 = [name]noavatar.png diff --git a/game/themes/Deluxe/Ribbon.ini b/game/themes/Deluxe/Ribbon.ini index 25fb22dc2..f86fe0b8c 100644 --- a/game/themes/Deluxe/Ribbon.ini +++ b/game/themes/Deluxe/Ribbon.ini @@ -137,12 +137,12 @@ ScoreEndCap = [score]endcap.png ScoreLine = [score]line.png PlayerNumberBox = [main]playerNumberBox.png -PlayerIDBox01 = [sing.player1]lyric_active.png -PlayerIDBox02 = [sing.player2]lyric_active.png -PlayerIDBox03 = [sing.player3]lyric_active.png -PlayerIDBox04 = [sing.player4]lyric_active.png -PlayerIDBox05 = [sing.player5]lyric_active.png -PlayerIDBox06 = [sing.player6]lyric_active.png +PlayerIDBox01 = [sing.player]lyric_active.png +PlayerIDBox02 = [sing.player]lyric_active.png +PlayerIDBox03 = [sing.player]lyric_active.png +PlayerIDBox04 = [sing.player]lyric_active.png +PlayerIDBox05 = [sing.player]lyric_active.png +PlayerIDBox06 = [sing.player]lyric_active.png # these icons are part of the tango icon set # licensed under Creative Commons Attribution Share-Alike license @@ -251,18 +251,18 @@ BGFade = [special]bg_fade.png # # # D U E T # # # -LyricIcon_P1 = [sing.player1]lyric_active.png -LyricIconD_P1 = [sing.player1]lyric_inactive.png -LyricIcon_P2 = [sing.player2]lyric_active.png -LyricIconD_P2 = [sing.player2]lyric_inactive.png -LyricIcon_P3 = [sing.player3]lyric_active.png -LyricIconD_P3 = [sing.player3]lyric_inactive.png -LyricIcon_P4 = [sing.player4]lyric_active.png -LyricIconD_P4 = [sing.player4]lyric_inactive.png -LyricIcon_P5 = [sing.player5]lyric_active.png -LyricIconD_P5 = [sing.player5]lyric_inactive.png -LyricIcon_P6 = [sing.player6]lyric_active.png -LyricIconD_P6 = [sing.player6]lyric_inactive.png +LyricIcon_P1 = [sing.player]lyric_active.png +LyricIconD_P1 = [sing.player]lyric_inactive.png +LyricIcon_P2 = [sing.player]lyric_active.png +LyricIconD_P2 = [sing.player]lyric_inactive.png +LyricIcon_P3 = [sing.player]lyric_active.png +LyricIconD_P3 = [sing.player]lyric_inactive.png +LyricIcon_P4 = [sing.player]lyric_active.png +LyricIconD_P4 = [sing.player]lyric_inactive.png +LyricIcon_P5 = [sing.player]lyric_active.png +LyricIconD_P5 = [sing.player]lyric_inactive.png +LyricIcon_P6 = [sing.player]lyric_active.png +LyricIconD_P6 = [sing.player]lyric_inactive.png # # # DOWNLOAD SCORE # # # @@ -316,9 +316,9 @@ SelectFrame = [name]select.png AvatarFrame2 = [score]frame.png # # # NO AVATARS # # # -NoAvatar_P1 = [name]noavatarP1.png -NoAvatar_P2 = [name]noavatarP2.png -NoAvatar_P3 = [name]noavatarP3.png -NoAvatar_P4 = [name]noavatarP4.png -NoAvatar_P5 = [name]noavatarP5.png -NoAvatar_P6 = [name]noavatarP6.png +NoAvatar_P1 = [name]noavatar.png +NoAvatar_P2 = [name]noavatar.png +NoAvatar_P3 = [name]noavatar.png +NoAvatar_P4 = [name]noavatar.png +NoAvatar_P5 = [name]noavatar.png +NoAvatar_P6 = [name]noavatar.png diff --git a/game/themes/Deluxe/Summer.ini b/game/themes/Deluxe/Summer.ini index dd3985851..9a5c7e1ea 100644 --- a/game/themes/Deluxe/Summer.ini +++ b/game/themes/Deluxe/Summer.ini @@ -137,12 +137,12 @@ ScoreEndCap = [score]endcap.png ScoreLine = [score]line.png PlayerNumberBox = [main]playerNumberBox.png -PlayerIDBox01 = [sing.player1]lyric_active.png -PlayerIDBox02 = [sing.player2]lyric_active.png -PlayerIDBox03 = [sing.player3]lyric_active.png -PlayerIDBox04 = [sing.player4]lyric_active.png -PlayerIDBox05 = [sing.player5]lyric_active.png -PlayerIDBox06 = [sing.player6]lyric_active.png +PlayerIDBox01 = [sing.player]lyric_active.png +PlayerIDBox02 = [sing.player]lyric_active.png +PlayerIDBox03 = [sing.player]lyric_active.png +PlayerIDBox04 = [sing.player]lyric_active.png +PlayerIDBox05 = [sing.player]lyric_active.png +PlayerIDBox06 = [sing.player]lyric_active.png # these icons are part of the tango icon set # licensed under Creative Commons Attribution Share-Alike license @@ -251,18 +251,18 @@ BGFade = [special]bg_fade.png # # # D U E T # # # -LyricIcon_P1 = [sing.player1]lyric_active.png -LyricIconD_P1 = [sing.player1]lyric_inactive.png -LyricIcon_P2 = [sing.player2]lyric_active.png -LyricIconD_P2 = [sing.player2]lyric_inactive.png -LyricIcon_P3 = [sing.player3]lyric_active.png -LyricIconD_P3 = [sing.player3]lyric_inactive.png -LyricIcon_P4 = [sing.player4]lyric_active.png -LyricIconD_P4 = [sing.player4]lyric_inactive.png -LyricIcon_P5 = [sing.player5]lyric_active.png -LyricIconD_P5 = [sing.player5]lyric_inactive.png -LyricIcon_P6 = [sing.player6]lyric_active.png -LyricIconD_P6 = [sing.player6]lyric_inactive.png +LyricIcon_P1 = [sing.player]lyric_active.png +LyricIconD_P1 = [sing.player]lyric_inactive.png +LyricIcon_P2 = [sing.player]lyric_active.png +LyricIconD_P2 = [sing.player]lyric_inactive.png +LyricIcon_P3 = [sing.player]lyric_active.png +LyricIconD_P3 = [sing.player]lyric_inactive.png +LyricIcon_P4 = [sing.player]lyric_active.png +LyricIconD_P4 = [sing.player]lyric_inactive.png +LyricIcon_P5 = [sing.player]lyric_active.png +LyricIconD_P5 = [sing.player]lyric_inactive.png +LyricIcon_P6 = [sing.player]lyric_active.png +LyricIconD_P6 = [sing.player]lyric_inactive.png # # # DOWNLOAD SCORE # # # @@ -316,9 +316,9 @@ SelectFrame = [name]select.png AvatarFrame2 = [score]frame.png # # # NO AVATARS # # # -NoAvatar_P1 = [name]noavatarP1.png -NoAvatar_P2 = [name]noavatarP2.png -NoAvatar_P3 = [name]noavatarP3.png -NoAvatar_P4 = [name]noavatarP4.png -NoAvatar_P5 = [name]noavatarP5.png -NoAvatar_P6 = [name]noavatarP6.png +NoAvatar_P1 = [name]noavatar.png +NoAvatar_P2 = [name]noavatar.png +NoAvatar_P3 = [name]noavatar.png +NoAvatar_P4 = [name]noavatar.png +NoAvatar_P5 = [name]noavatar.png +NoAvatar_P6 = [name]noavatar.png diff --git a/game/themes/Deluxe/Winter.ini b/game/themes/Deluxe/Winter.ini index cda26287a..d3b5d165c 100644 --- a/game/themes/Deluxe/Winter.ini +++ b/game/themes/Deluxe/Winter.ini @@ -137,12 +137,12 @@ ScoreEndCap = [score]endcap.png ScoreLine = [score]line.png PlayerNumberBox = [main]playerNumberBox.png -PlayerIDBox01 = [sing.player1]lyric_active.png -PlayerIDBox02 = [sing.player2]lyric_active.png -PlayerIDBox03 = [sing.player3]lyric_active.png -PlayerIDBox04 = [sing.player4]lyric_active.png -PlayerIDBox05 = [sing.player5]lyric_active.png -PlayerIDBox06 = [sing.player6]lyric_active.png +PlayerIDBox01 = [sing.player]lyric_active.png +PlayerIDBox02 = [sing.player]lyric_active.png +PlayerIDBox03 = [sing.player]lyric_active.png +PlayerIDBox04 = [sing.player]lyric_active.png +PlayerIDBox05 = [sing.player]lyric_active.png +PlayerIDBox06 = [sing.player]lyric_active.png # these icons are part of the tango icon set # licensed under Creative Commons Attribution Share-Alike license @@ -251,18 +251,18 @@ BGFade = [special]bg_fade.png # # # D U E T # # # -LyricIcon_P1 = [sing.player1]lyric_active.png -LyricIconD_P1 = [sing.player1]lyric_inactive.png -LyricIcon_P2 = [sing.player2]lyric_active.png -LyricIconD_P2 = [sing.player2]lyric_inactive.png -LyricIcon_P3 = [sing.player3]lyric_active.png -LyricIconD_P3 = [sing.player3]lyric_inactive.png -LyricIcon_P4 = [sing.player4]lyric_active.png -LyricIconD_P4 = [sing.player4]lyric_inactive.png -LyricIcon_P5 = [sing.player5]lyric_active.png -LyricIconD_P5 = [sing.player5]lyric_inactive.png -LyricIcon_P6 = [sing.player6]lyric_active.png -LyricIconD_P6 = [sing.player6]lyric_inactive.png +LyricIcon_P1 = [sing.player]lyric_active.png +LyricIconD_P1 = [sing.player]lyric_inactive.png +LyricIcon_P2 = [sing.player]lyric_active.png +LyricIconD_P2 = [sing.player]lyric_inactive.png +LyricIcon_P3 = [sing.player]lyric_active.png +LyricIconD_P3 = [sing.player]lyric_inactive.png +LyricIcon_P4 = [sing.player]lyric_active.png +LyricIconD_P4 = [sing.player]lyric_inactive.png +LyricIcon_P5 = [sing.player]lyric_active.png +LyricIconD_P5 = [sing.player]lyric_inactive.png +LyricIcon_P6 = [sing.player]lyric_active.png +LyricIconD_P6 = [sing.player]lyric_inactive.png # # # DOWNLOAD SCORE # # # @@ -316,9 +316,9 @@ SelectFrame = [name]select.png AvatarFrame2 = [score]frame.png # # # NO AVATARS # # # -NoAvatar_P1 = [name]noavatarP1.png -NoAvatar_P2 = [name]noavatarP2.png -NoAvatar_P3 = [name]noavatarP3.png -NoAvatar_P4 = [name]noavatarP4.png -NoAvatar_P5 = [name]noavatarP5.png -NoAvatar_P6 = [name]noavatarP6.png +NoAvatar_P1 = [name]noavatar.png +NoAvatar_P2 = [name]noavatar.png +NoAvatar_P3 = [name]noavatar.png +NoAvatar_P4 = [name]noavatar.png +NoAvatar_P5 = [name]noavatar.png +NoAvatar_P6 = [name]noavatar.png diff --git a/game/themes/Deluxe/[name]noavatar.png b/game/themes/Deluxe/[name]noavatar.png new file mode 100644 index 0000000000000000000000000000000000000000..00bf0f0745d026573420a07dc1bc172845e5d99b GIT binary patch literal 2550 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 Hu0mjfMi=2I diff --git a/game/themes/Deluxe/[name]noavatarP2.png b/game/themes/Deluxe/[name]noavatarP2.png deleted file mode 100644 index 3dd7ac3456324e32237a3e43748f0c7c34ac12f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1019 zcmVP}zWI`5=N!Tcc*?6=^d$dP? zAX?;?2zj}dWu7F-p>i6Xu5Xu#KNB(S<0ted7-3s1Q}sU2Ppli)#xH zeZ2qE_T7a6I=rIQ4V^lVmf5X){O>b7Zp8aASN2_c>gZ9vh#mBOxTw0W61MNY;d~8V z^fS*oT`wn4IPs{Wsz*^3eB<*XFM-SeCCL2GFt}u#pXg{pkn8E(_7& z5Cdp|v62|aG1jL0j@TZ0x7$Hey02P=ZiNsJqni}>{1`tD(3-=+{PT34LwCB{#w_{- zBRQQ;=Fn#t%beOX3*EqErdORgXbHoSQa?H{jSd3s=osb`Yew&2BRj3=J?vz!5q*TM z9JQfOu$Pl2bOoEKw4iI)OROxgJ7)COhHGWNV5u^keJWw`ZS4ow8(56 zDl3zV%v*E;suau7pi9LO4VLJZBN{Bx(K2*GihlS}Vh$R_@o3Tt<2o7((1IblB{x4U zFdMf!$MdH4Xd~HjL}MO0%NU{ohUhe9i1s(4AE!OqqhC*cIFD8yZ7HR{5eesO{$QV$ z9MSYmU2{Y$kG7N}NA%M*2^*>R z=-6Xxb4Own>DtXqKQ=Eww=F}oQgzJ`tyERn*gjtAMcb(}N;_JyMDIDG6-%_~G836V zD+e0dquoVUMpiw`EZg_!6wW0bL1#nYV4l^Q9-Zeo98UfO?LsMeQ)qsn1>DciGP41P zG#t_R{&pZH=KaP)-|36a`TJ{}+!80gXX0O!ou&pl!8I2$^opd81u)ZByW4NJ2sklSFhxM|4DgAX=+a z^my9tYAJ+}ZO~|ibJFH&vluy`f#zyc29B76uO7{VxIwGmD|ZE;c<~pG=iot(Ke2_* zgzPSVEdV@j>b~#$9<$S*WBx?^{%4=;1%To1j?*+vSe9v;rfIT(7ARDs1c3DZj%k{v z3(;wsrfF&geOVv>CeL%MO`hkuVf601Rn*NcWm&>d%CcOA9<LT>0s z2S`M_sxYCWu>h?Zq6hEhy9Fi*`{20W)DdkZ1xGaALrcjJ4KPGk6+?795#x0_q9gio z=ST2pifBh^;)&?7zvdqv(<4W;|EBIZqA8*sWzP|PcT7U!Ye?Jd96z87A%x5H`{)fr zbPk4SM6?_9o+BC&?FgO66EULy8~Vr*O)SxhBbw#?w4-?d&r>q|jtGkAV9`BCwEw2o zA2No?e|_OMZ1cgI7cbF%M#A-Y*HqsZ4xmlg9| zyfw94H%rb#eW|Ky?IcRavl)aCCg#|>oROGire-tyzcw#G4+BFqQP(j<6Sb|2>Eo?g zw3|9Z#?izQz2%4|mT22$CbNO202(@?!$r5=OUsf4hW$76r=V@tm#r-{d2i^j(784A z=)IvApf?QB7H>X!TWE{D3G>%e4CsEEETQ+{hOX-xtkA84ui|FWotr+7&K!Dh*SRzp zLkIAP+ zn;dS@+W+D@006D$qXz_&VfHF+2r2W>i{G+fC$q+T6bLNEYjlO+QawRSDPddG$ z2@xr!5OCBTbO@#7MWMMtYlNS_WhMcK^c>N+emf8o^!3|;n4quA3>)<2EDS#Ai&4pp z&|ryfIHG@guvwwMiwrOHxyUd>pWf5J41Idm$PSH?BN{u7XwVnO(c`O|igzyn0000< KMNUMnLSTYt;{5gi diff --git a/game/themes/Deluxe/[name]noavatarP4.png b/game/themes/Deluxe/[name]noavatarP4.png deleted file mode 100644 index e0b164ea664ba980994a739d11ded9a065e13e6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1015 zcmVYr1ffx9{Z13D}7ZTApex8$Hem)+yTS@{UIBxwKUYy{=jfn+`sQoTx8Oz2kElWiQBqw> z&C|C=FW+4E!Q|Ap$UhKo_3k|3sw!EZ2<(1Xo8~rW|(f%d!(NA@J=v`qL zVr{z52=$?NyB#E@`>0jurrR6ZpRZQk#-G~`keb6m{q=O3LU!g~{?0_7pd_c$NgetO zWtmfZW}$1S%=D@=2i-z(+EzZ=Q;qgK>F5yZ6G}$!pd&k}=somgFA;r&t{kPIPtcc> zBy<6tDWsq)=uK5gKtp@lcC64?iuSy*LNf&&z(nd*=*Woqyl$U1Vje9r8;i=yvdTci~H}tC=#G!Rn7~9cUfG!EqjdAl}f!Wx- za|}1NLo3Oa5si81Bq2ltgy=XXM7smgou?hzpWUF999mP3jOf8J2^&*G+9v1x0bK|-oTlfaJwkK{LNpv&5BkW6hC^#Yhweah=)Znu0&E!?wU{j`{ z7vE(;O(u=^*ut`G-=kw#mv{u7TmlO-RBL*4nx?Qg=>Xb>Qu3zI{6d$oKR?UN1`Meg z(fEFMAQJTVy91G+zn2*r^z$qX9`w_wWFj;u(K#df(SuEez84uT^sUH{p|AI8AVXjO lYotT>MTQUE6&XS_#6PEx@6YP?``t?J>Tm$E+jRVi~9R%#z=O$wo!}DNJ+9RQ-{8h zvTUh6o6t3>%=)Uc1>KV3w5@!!uNv+9($OKQPbe9EA{{wNMW0De&JxjA(v_<;bVmA; zNkSK-Gldj%MS4?J640bQZM#%xEJgeNQlVLc4#-66UFgV&`L?f58!;~y8PB4!vbxB8 zM5kmbsW_U9S*;i?OCo_N|gLP+#IW-n)oW^J<1Die#*@ z)wv=u2xP5hHh*mvKsOB_n)9jxL~~vgI+l+Ya?yI~4401PkmxfannR*hmzh8Xnp@D& z4s9>GFkV_jHW)VF(BFbqSzpeo(A;=K+l3C5p%RB|;1fDAwh=;-2%=MSd$;wEwYqFZ=Q$bA;+q^^(WUv#b?zz4_2`DINwazvH?k?~ z(86z7P?J^jJ&t5qj-SynS(kVLo!mnfX0F!sXvP>>95#ctp_F_mG%x6q?9abt#v_E( zh-i9#I}ix^`Rza;=;ty6gMM6vfrEaSl?;R?NOX>f{vE-FLQh2o3q2MYF!b=A1~ByS mtPu{~7a2TsS7ZRu#Qy+N|KmHbJNowk0000vFO%5C&lH{}z{n5k*c0LUREh0d(vqB8McKt$F`+v{UD4CS)-raF7IZ@Mw?rXpjCv zG|z_c_VGKOGR6|_Fgk-VVL80fCd2ED0ve!Kix6m_b6y_1A56Vg?VJiJE7!Y+@~OsE z{aKlvKM?Qea6qT3W3Uid={~wm$L_%%Jv^szAXX##$RhPpEc>MLP3)li;erjlBpLsQ zncT*}o7^5Tg^pNS%O`X-JLWYiusF>a{RiThba+|bhwYdAbb)R8>0d z|1j*b5gqn1fbN^R&(vrg$1&_p_ZhKnbbEPM#ZRXbXiE1{yU>F3rxU#XoifYH*YWNG zT64LWKcCJr(4Fpl*etpLBPoi)9Qp>vvZnT|LN{PC%d5^BbPI;lw))Y5X><^1N5^13 zv1arMY~-XBeFi%@YeZkcR<7F61=vfW30;BBR9etA*iBt)K!f$P?OdU86deR}g=Psl z0*o}f&Cu)_`U4ShzUB}1>6RiIzo~1AXz9_Ga;1o#T$6CXTGF;Sw=bANaABBUj}8c; zV<3nIkG6xpQbdDCTSCYFK=kOphQ3imOOoh}BAVp+w55Fj^EnaxjtJ7Dokcek(fCcx zzhn&)etn_0Y!l$J35~lFA=$XR-o2S#$I&50^yb|cf+8BRsX0M(xnoBW)=rll`&`hP zI^DNR4xzpzNwRknrRCiWj4>U1Y;EpHj3QmTneoTw4QL?}5iLogO_!O-1X?=K&>rnBxI=002ov JPDHLkV1hjU^(g=V diff --git a/game/themes/Deluxe/[sing.player1]lyric_active.png b/game/themes/Deluxe/[sing.player1]lyric_active.png deleted file mode 100644 index 089c8c5e7a04e3c15c3fe75b375c06c02c4b3e6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 884 zcmV-)1B?8LP)-tLTV-tOM+cXxo;^6&R)zg())$0(RG<=X1fI74i+JbFo6NT%Oa}I7yW#CFRlK7Ezkt`W) zr!9;$j4Hge%OVA~JD7L#+z70|o?a6t2Gbb-nm?adD0(m|)ir%H4NoO}csw!<3Bl>4 zfpncE(ccekvoQWe?Mpx@K*}|Nt9rW>P-b9Rlr^b>xmGY&p*46-!|9i_4>pW7SE|mD zKFVN^L`A?d5Y1TxhN!dpU;sA%>Y2Fu2j3G!$qqCVViqy5nP|9lV3Ub&XSS>ey*O3C z*jw9&vjYORkM`j7-j3RybFcFFlyRZldPf*6&>Rf2brx!$MLGd(A2`0$o>J*i?@lW+*E|KEnuMiDPd zPyGAUBAY2<_~C4OT}7$-=;tI%^Eg;W=eWNI9WDYZxr*m2>}gJp zdtZ01>PEFA#O06toY!~Abtl%w26zMf|ABP_ap+wo*BkstfB^ubn7HBFF|(Nf0000< KMNUMnLSTY}^P1uS diff --git a/game/themes/Deluxe/[sing.player1]lyric_inactive.png b/game/themes/Deluxe/[sing.player1]lyric_inactive.png deleted file mode 100644 index a349007d9d19627e88ec6fe5023a9bbb95597352..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmV-k1EKthP)O=vX{gr}2mycOY4vRU<$Jt5sm$GLjqU?#({s}r zW~cPNgQcKX_)-O1NwyU>YG2}npqcTO zg<~KhAe{%(1e)23?TRdx?6WYV;mX1!EUk5KKH8`~)vX;1DlsE3o6HpjrDzjCB`a0G z9x18|h=>H1vl9{)>k+KA>>b@n90+5_+B4~uvhqq$L<7Trh$h@ zA_@AD11tW`bJPb9ifovN=JTC`IeA`!2^nU~iiP}T(i%q3<@snR=-XBbtKZB3_}RLK z6S#+b^Z?#84Rq3iFtJ;)l;sh$$3mPrqCMf&Z3e&4zh7uAP1LXAK-y480R8!L&uqLQS^aJ9=a|cziW@3zgM;r|L%3O`NRnS=s5sB o0RI1g=?}lbL#_Tc@J|5-08)I4J{+NUQ~&?~07*qoM6N<$f)|#Ic>n+a diff --git a/game/themes/Deluxe/[sing.player2]lyric_active.png b/game/themes/Deluxe/[sing.player2]lyric_active.png deleted file mode 100644 index 509767fa2c4b8a83956495b29ffe7bebdf56de67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1034 zcmV+l1oiugP)Jw4Y(3q2yR@6h9oPEXv|(Q3T~7czuSELVbDmTi^heh zGXe84q^Iq1`u(cB_g44oH{IjJ5oADeAD3IVe&^m(x9X|$mAVVYYf$A7fWm(vE{T(@ z)AvP}dTb18tSo)f zW=nIkb{oszoWboC>u2gXG;Gq}K>F}&--nGN2@$r958)q#VhLWk~j43A-rk3YS~XgaGj z>Tax9n08V}8R*RRYzo*+#=NH9a-X#{X8pPf1yTxYMku4g*|6|t1n3fCODj07VU;F2 zv=pOP-{#NumTC40(`c$juF!uimI{coPAHpz0*Pa|z>r3y0R%fq~`E zzeID(ouUhmJ(2g<_spWcZ*T7Z_T2MmZrhHZ9)2Y6tK2(Hg-68|(-A}oTsIPBOk=6) zP%^7SF8WkBeZSjm4zikPNaT5dU!Re=@xnr({4FeL8o1)Wgt->uw zjlp?kh4I2XOPl_B^oVHA!pscn56%qMrZGB8m!TqhV7mT+(NP7ADH(I4C>gWR51PLI zcw^?FygzNVq@Vk4p5236AKyQ6)~(a0kgP1r9T&9;Y3eiP3vP#iUBTy#AGmaNjZIG? z+`0uj-kV#y^51?7i?1D#S59XhP=NViVPK}r7BcM9xSss};XD>!e+9Sie{vNA7mmFu z_Tuq33-tL6ea4)5Ci@#F!0FJVH1MyTI)NYd?OrngzBYaNp5}DUeYbX`T>{T2a|wW| zn}I!$9iqDEyOHrhWl!Yo>62fLjIaFiGtNxi?VTu+eK=zlFfMX zR;_h$B6uxUus|Y9 zBt$)k;UIV+7*CoQPsG2V2NR=5e}q3quf{}gl1MNyK4Lr=vnY_Dfd!U;%RXkh+p3=K znSFs^Vn_@PwO!pcJzsrQ)wRps-hZfB5CHQ$_*X+q*;GP&QGdGS_cXvT6b*!8ja2HbhQd;wXK(JnDrn+g>t@vYS zpRLXJ{6?M!QiqEKfFMJH<`q~kuP=SeUESS6uw1#|pTO@KA!oh1dR zNikyLVs$J`{Bv11Cd3p}mBjk*?+OaC)DoF8i~yIE3lxkZXGYH);)x@OiHv#I9cF!f z(0}dffmn^wH;7hOjV_8K5L`G9yiiDTPXOphw7w2;xtuuQK)H1QF-77*2QaPK!Ql@l zE}Hupmp36dHI)TX+gRdxe|#KY!OSxzl~hNZr;dnbRD-|^yMXDCxw@4dsD1iubZ>YF z`lc@@Oh-qPCSsp!d@UK4Jl_xb*%^}qK2QKyFJl@g1u6kYLWC(aRvl4q8!lu{w|2F^ zpYGE&Nkc~t;PBE{lyV&Zvo4N&_8{pZ9lF2A8SzizLd08 z?Leh*z&K0{R0pu}3iN?h9c^qteP!9OOe2GZLwht6XgXq*buumNmkct7o}tbdN0n{Z z`}n?h&!(?F``q4#cM$LG_U7ZjcKFK zFCE`|w{!0MF>l84SCEVFT@L5mbLQU9J@=k_M^sLPY6KGFK~fNZX4UUi_L7N8asL2wHRo$L_8(4mvf{V+E{9^uo}_;+cU!kfj{ z==yz~r|Zl#$ZO)@s1t9|p!Z5-)kO(3y)Yz^N6-pvL9dZ1x@-NUc(*;u+ zCq?boZBp0ub#GgUG9aYNibX+b(x`~i6NfITyCA1HZkMCO?1KNW66L$J~7b1X5ZV}E*==WEgTA*W?YGRTmmwG>h$_9k=Y zrN!mzbJe=>ck-?0uMr7U`?mL)HLS*cmSdhTDSsnNKt5y3EkNNbE@Qr=XgKSb(=iJ1 zX(WNTvmm;H9aHV-Kzq^Hx?nv}Y1>cJBS__PFlg}x0bTKmOPa-COZYPO#Scf07P7ID zMdNteKkg_L?x*glM@D5#hibZ&?_DQT7J=>3x4$C<%hj373TPzvgut@w7`N3UKYDDbs2rr zJ!Sq}?8$}xK}bu(rhb}L^i*szd<(n<{_nv2faK|3>6;DyBftPcK&4g#5$w|d0000< KMNUMnLSTa6qu@CJ diff --git a/game/themes/Deluxe/[sing.player3]lyric_inactive.png b/game/themes/Deluxe/[sing.player3]lyric_inactive.png deleted file mode 100644 index c5a00600da3ee20512008fae378ad791c19937e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 911 zcmV;A191F_P)5o$cBi3tb4fnGd#^+Wh!^ya~sfER-Y4-oYN0x^XqkU&eVe`wS9XUCo0 zeXsAmmZp%%!LZA|ot@p;-^}dHG_dDipW!sW3PrmJO!j}#eu|b`ckAEsLH^}~&ZF1F z;}d)mO1v~wkf~vT#Vq=QEj!V6*cpAf>_l_J(`!2ALf6^lJGYiPA6~u9znPzJKYq#) z1sq^wB+V@}umpsb1|tCE8dlO7qFF}qGBlerQ+yik=I_DB&eD^`+`CV_C0bwy_Q-Ct znFiNDwz)V1s`t%~q8-$8(JH}=GXphId%@e-21^|8Z6jy}oKY5qfTV;&U4iO3zMily z!3L~f;eY^$#|xGKgcS3o90D@v&0y|QG%7wYJ;F69Af>Zh1{6SjjbMPIZhp={5n z_q9xS(S-)qp)U4~LUBdn7F+#^JIo2!d0EN$=)(>Brm>y?g!BQZcf1T!>JkN>wmh_$ zrGOdhY1hOidwtYv@pEN@yilQKhrUN)U*^D26@YD7c2Wnk-xCE)P_IBD-J} zz>|GnDnoTY5|tkhJ$1KFMHa!hI~LVxi!n{((UupSg8G4&%T)&Mhq6@Td`zX&sKJ$P zThiUMS9Z#MdzUt)8%P^VU2Q}4JekU*3N*>fd4;W lF8^%_WK!Fw4g6Pt0RZytevW&*D@OnT002ovPDHLkV1k`FsS^MI diff --git a/game/themes/Deluxe/[sing.player4]lyric_active.png b/game/themes/Deluxe/[sing.player4]lyric_active.png deleted file mode 100644 index 993041fd5b6af98e097c4f6bf074cd7922802e34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 966 zcmV;%13CPOP)LY-cO--u z#U`3F2x6lWf}MqsMxqFkh@!O?DJ(Pw3sI413PrHfMr{%mQ>dL5(Z&dBp}{~jCU}D6 z*W_;RXK#1MH#2*8yLVn<4r3viu*uEFEjm-d5fz@dC%w#U} zY3QY!N?eXTU_)F-zMtR2!cDuEev=Rs1^-nvP6eXrKvWGt;k^`4L#Pjm(BV3kNkDe& z4a_~d$alNcG{7#9__-DqiqtG`b{?*IAgW1#ZeVW!XtF1IxLiEFHTnu>A726H*aua6 zdIIQz{>R5g6!esP$)TzT7MrgFVxZ_Kp$LlXsS)wU5l}X5gJ?@TB!&misLtXHMAC6@!jd&Wdhu&0Cp z--|zU;eVp{#?;esIIfGT3}2+xget;8@xZttHzFXOPJkiU^H~8_NR@fw+5irMb8=Hc zQHW1GuelMzxV$lWuv+4>vLOcRua8yaB?o%Ie9|WeOu~)ULr{C_USM6~ZWq|`k)R<^ z025G4JYMbuqMg0sdeToNTxn>8niDt6gD{r?YMC+eGlB1?1SU2+XZ1P=E zk07qs_n!p~jo{6C)(6b|3CfmTa*Y6+fjj0$E|@$87TusE9iPXns9#9T7vl;gRUd;; zN3RU>uWwA(2U;n2=1Nq)m}_!ei$*)iK}rHu-sD$G#%ioQJxu z{=#_r))BGJUlUw62M*pdxR}4-3ogdN2yt8%1pCGu)_C733!L=F=TO_;EuQDNr$4?( zm~Sh`vcS%!v9!8q5JDYSh$u84ItVk=;HDa;)8-qWE2q=&LzudA5@NS!mreD_)6HPd z#j(cgg%3({DlUSutYrGfDfBGf+gA0rnD6D-mON(5ODiuMmIU+rs8q{GkChkO`UBn2 z&?y7%+toJ6PK|p}iuIk9f`eBKfgvfMU#j_n8iT9kT>+O0amTCli>LDOJ5_#|{zh8^ ouYvzRFfSk$=9T`h!M_9;0Ocfxk#S9gVgLXD07*qoM6N<$f}Hog5&!@I diff --git a/game/themes/Deluxe/[sing.player4]lyric_inactive.png b/game/themes/Deluxe/[sing.player4]lyric_inactive.png deleted file mode 100644 index f09669b224a41ad1dce3c4f2e4c9a5606b521148..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 861 zcmV-j1ETziP)wa_y_zEp8Xwq^JK!!=)r>?ghWIS7!sc-Va45LXQ#WY>h5_k z!($Q9R)Ulycr5_}Ub9qAFO}1IX=l=l)UVI8-FMF)4=p~u-MMz>o zghKm-2hA2k{}Mn%jsV#^3E=J$er3=7q>cNI9YL%q$UZy-xX8lqy+=1OmHFpb!cllw zV0)XfHwxpf78-L$M9$ovrA}Gi1Z+cALKaaSqD$9z#Y?_@1ikht2d6(dau(FYtSdL0 z@d4$@&6B0Q4tnK{ z(^GeolXG@0N1A{hST?7Lq;j#{YJsSaI1rN*fVZ<&J2B*aQJ1Z^erIds@}YG=?KT)~ zI+ANE1eAa{F>L7w&A@Rb#@SQ%O7$dYU&HLhAWqMd#GYATl5tpv8nNY^GUQZDu~%c0 zpbRMj1APKEsZE-MKG{`P3FU+>$aEdhQ)5<5eVa__K3-kep0@;Uj0V)DdQS?B4Pu3H zsu3p}P`i4w;*j24x9oV|=PaiVXdNopA^{stFb~X!LgL(VP)ZIvPmEcl7`x;V8~z8|@U@F<~h@Mz#eaaG|#O9@duc(q;Gh zhQuAV_-R>WQFMW7;8Q8AuYKrD++WR>PQ6s5V5)AJk{_8D;Um%&||zG?bi0UE$}l`{n%0!I-l z5CSJa2OV*M`=~hzL)&z~%AN;NoIYy`ydn_0WQ*Ux%TNmeOpqf2I2s~Ao7Tu?LfXPW zjQ||A@P%{;k2ogo;h{ZHRW#^Frk>GN(tGlPF4S|bz!-tzoE#)gm@;C6@3~MU;TfnX zrEskw640RFYiFo7B)ZvponiMw_rTY-aGR@RP%cbDrt1bA?tR?$yK>Th6LUmG zEEt%HzF~CeBTmnROpgdk9jd>E-<6|u2%UGrq^C*(^VrjSmDf;S9+kG49f4f$jI>jS zu0q{?+WgL4I1Pcf&}=548PBP_Gyy@UL}E&E@%F&P-f%7&AEI?54=j0x`S??l1XDnu zfmEcSZjx)G+6rtUXL4c0etf?Kj<$%jTS)A*p4;8e!O9FZ@2IRlgN>i#n2xG?Y90}= zNiIJ~Wu7l^5U}N>Zd}40KfWwG=zqNy~*qp#l4*Gu( zr|Q0ft;!rW{xOx~;uGl%#}u3zEGiykE+P1B1PjnfAvNPc`-pjmPPVx|1;v#~2~=_C zTALa&y-}M7E7gxt<`K9qc|zSQtaBMLY|}F1^9n|`_+@lk2fB|>z>%Z3q`mU(8aDqa zFs!@;l}>m%9e!QsSeF&-0GRr3yIx)`M=U?q_d-g1xa%*Yv%0@ab~k|&Pc0Jv0h0US z_*I;eIqf+y0R}#6^SLq7;*UuvEI&#_)QHOL<2xq!{`})6zJGLXhMj zK2R?ayzIrRARYw4ga1G;{sDi4KSpn!1aFdyf*$mwuQ#hbZ$akfv(@AmCp-M*k|y^4kY(I*$kX&Qm)kWpkDj8 zK#0Ui5Gl7)-?kDYk2fUG1CDZ8?sNx-FQ0UhXLtMN-UEqf0QL9damg8o1eSOpbP7lZ zfDRlH>-^PUFQMVS=U7TN(feOe8EFpq05BLo2DD zr0bBku7c@*c_h*F!7LkF=jH&Mv3p6H0Gcx>y_owx_ZYr)Bn#c%TUO6RUzHwtpOg`4yjN3)K z3t-=-XR5up*Mc|7=JDj3bBU;O7lw31(b{m9#{CtAq6d`?$Jn{na*V*!mroy#_FpVi ztyQ`0-lcA~SWiy=IR!oi{{MjOf2pRw!Stkoe+w`G2smat&#OW%00000NkvXXu0mjf D7OIye diff --git a/game/themes/Deluxe/[sing.player6]lyric_active.png b/game/themes/Deluxe/[sing.player6]lyric_active.png deleted file mode 100644 index 65133d034232acec6a606d96140e895cb9a5efd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 960 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 literal 0 HcmV?d00001 diff --git a/game/themes/Deluxe/[sing.player]lyric_inactive.png b/game/themes/Deluxe/[sing.player]lyric_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..3a1f8a4a9e25a0786fe34a3d849fa92cd915e699 GIT binary patch literal 1865 zcmbtVdrZ`J94`w4rlLlcfC#RulS$FH@GP106^q6sq7LX(`8F9T>EziEeI%BPV1J zKG`vkVW=EI`2Bv|&)|wyMo>1JjUZ`)rY#U*(W_*G4_IV<&Nzbu=z=DxhNQ@-$;g)~ z6^0#y-j0RgQ^T~f9+C=*Mg(}3pm5R*D+m;YFix$|yg}olKmafB0ol+YmI`B4k76jg zNBJA-@aIPiKzW_cu#E|Q@%h3gbYp2H3?n4SglOGWr2=9F(3J{J081;On{&))R4!Ws zyrF0=Me&X&t8m<9l+M7d=scL4bdshraUagGG=l=h&9W3oMbUYyBw1Aadg?=5z`+~9 zj+vV*Bx50I7n#A)3}>~?Cn=63$DmF{l-yMlLNhoj;}IyVD3Le#e+7#I=TJ5AxePOq0pvUE7?cB-B#~pRB+E0Uq$R^`<1IEf zOIdh|Wh|oA%JXg;Yb&L~`yGlUT6$~+#%TQ>07?HL-D5CaaT|VK1B=U`TK}ig2pAV( zcJEk-#9yNietW3X$Kh~%n9K0&f)C;82V^+%G&tMTny7vRiRjLEEOzDX8gfzVKQ5f| z`>AVlsdTZsKeZ%lB*ng{di&wy8Qov6eIt4vC7z!V6Dgb&MzOf}>)sm9e5Eye{`9-6 z?u^_y7GK?S?W+rJhG{P7UA}ttfsxIfqcOGLfwtna{FKJ$s287Z?zmC4cKE~LE!7_+ zZBd$1E)P}SdwbxzweR*0*4fhaQhD8L2?Mt1FOjWiVat!118F_W4*$^b)5wG5FWT9a zE1QuL2EVu{;z%_1dS-(&C5~z7>#aPqDJt^QW7LsE{leY_wX>)0h*J@AL(;3gZarE! zH|V%@YH0Z9UvI797Ddl|f;t_Q6g#W`sgD2eyfOC&z5dxslr1_Dp2X+^Iu{x8B}$GiQ*^xT}7#$8bJZtdA;aa3+fA3R|@xxc=T(o?I?J9gF#)a+QEbD zmG+K%^~VP)Hl6CX7PWu2U|myZ+b^SC@6n^~ypR4!QE$)cZqW{HJ|{ic9dG{Z@|PAk JPAn;@{S!GKhkF13 literal 0 HcmV?d00001 diff --git a/game/themes/Modern/Blue.ini b/game/themes/Modern/Blue.ini index 7933ffd46..04ae51fd4 100644 --- a/game/themes/Modern/Blue.ini +++ b/game/themes/Modern/Blue.ini @@ -137,12 +137,12 @@ ScoreEndCap = [score]endcap.png ScoreLine = [score]line.png PlayerNumberBox = [main]playerNumberBox.png -PlayerIDBox01 = [sing.player1]lyric_active.png -PlayerIDBox02 = [sing.player2]lyric_active.png -PlayerIDBox03 = [sing.player3]lyric_active.png -PlayerIDBox04 = [sing.player4]lyric_active.png -PlayerIDBox05 = [sing.player5]lyric_active.png -PlayerIDBox06 = [sing.player6]lyric_active.png +PlayerIDBox01 = [sing.player]lyric_active.png +PlayerIDBox02 = [sing.player]lyric_active.png +PlayerIDBox03 = [sing.player]lyric_active.png +PlayerIDBox04 = [sing.player]lyric_active.png +PlayerIDBox05 = [sing.player]lyric_active.png +PlayerIDBox06 = [sing.player]lyric_active.png # these icons are part of the tango icon set # licensed under Creative Commons Attribution Share-Alike license @@ -251,18 +251,18 @@ BGFade = [special]bg_fade.png # # # D U E T # # # -LyricIcon_P1 = [sing.player1]lyric_active.png -LyricIconD_P1 = [sing.player1]lyric_inactive.png -LyricIcon_P2 = [sing.player2]lyric_active.png -LyricIconD_P2 = [sing.player2]lyric_inactive.png -LyricIcon_P3 = [sing.player3]lyric_active.png -LyricIconD_P3 = [sing.player3]lyric_inactive.png -LyricIcon_P4 = [sing.player4]lyric_active.png -LyricIconD_P4 = [sing.player4]lyric_inactive.png -LyricIcon_P5 = [sing.player5]lyric_active.png -LyricIconD_P5 = [sing.player5]lyric_inactive.png -LyricIcon_P6 = [sing.player6]lyric_active.png -LyricIconD_P6 = [sing.player6]lyric_inactive.png +LyricIcon_P1 = [sing.player]lyric_active.png +LyricIconD_P1 = [sing.player]lyric_inactive.png +LyricIcon_P2 = [sing.player]lyric_active.png +LyricIconD_P2 = [sing.player]lyric_inactive.png +LyricIcon_P3 = [sing.player]lyric_active.png +LyricIconD_P3 = [sing.player]lyric_inactive.png +LyricIcon_P4 = [sing.player]lyric_active.png +LyricIconD_P4 = [sing.player]lyric_inactive.png +LyricIcon_P5 = [sing.player]lyric_active.png +LyricIconD_P5 = [sing.player]lyric_inactive.png +LyricIcon_P6 = [sing.player]lyric_active.png +LyricIconD_P6 = [sing.player]lyric_inactive.png # # # DOWNLOAD SCORE # # # @@ -316,9 +316,9 @@ SelectFrame = [name]select.png AvatarFrame2 = [score]frame.png # # # NO AVATARS # # # -NoAvatar_P1 = [name]noavatarP1.png -NoAvatar_P2 = [name]noavatarP2.png -NoAvatar_P3 = [name]noavatarP3.png -NoAvatar_P4 = [name]noavatarP4.png -NoAvatar_P5 = [name]noavatarP5.png -NoAvatar_P6 = [name]noavatarP6.png +NoAvatar_P1 = [name]noavatar.png +NoAvatar_P2 = [name]noavatar.png +NoAvatar_P3 = [name]noavatar.png +NoAvatar_P4 = [name]noavatar.png +NoAvatar_P5 = [name]noavatar.png +NoAvatar_P6 = [name]noavatar.png diff --git a/game/themes/Modern/Winter.ini b/game/themes/Modern/Winter.ini index a72468c78..e5c7cd7cf 100644 --- a/game/themes/Modern/Winter.ini +++ b/game/themes/Modern/Winter.ini @@ -137,12 +137,12 @@ ScoreEndCap = [score]endcap.png ScoreLine = [score]line.png PlayerNumberBox = [main]playerNumberBox.png -PlayerIDBox01 = [sing.player1]lyric_active.png -PlayerIDBox02 = [sing.player2]lyric_active.png -PlayerIDBox03 = [sing.player3]lyric_active.png -PlayerIDBox04 = [sing.player4]lyric_active.png -PlayerIDBox05 = [sing.player5]lyric_active.png -PlayerIDBox06 = [sing.player6]lyric_active.png +PlayerIDBox01 = [sing.player]lyric_active.png +PlayerIDBox02 = [sing.player]lyric_active.png +PlayerIDBox03 = [sing.player]lyric_active.png +PlayerIDBox04 = [sing.player]lyric_active.png +PlayerIDBox05 = [sing.player]lyric_active.png +PlayerIDBox06 = [sing.player]lyric_active.png # these icons are part of the tango icon set # licensed under Creative Commons Attribution Share-Alike license @@ -251,18 +251,18 @@ BGFade = [special]bg_fade.png # # # D U E T # # # -LyricIcon_P1 = [sing.player1]lyric_active.png -LyricIconD_P1 = [sing.player1]lyric_inactive.png -LyricIcon_P2 = [sing.player2]lyric_active.png -LyricIconD_P2 = [sing.player2]lyric_inactive.png -LyricIcon_P3 = [sing.player3]lyric_active.png -LyricIconD_P3 = [sing.player3]lyric_inactive.png -LyricIcon_P4 = [sing.player4]lyric_active.png -LyricIconD_P4 = [sing.player4]lyric_inactive.png -LyricIcon_P5 = [sing.player5]lyric_active.png -LyricIconD_P5 = [sing.player5]lyric_inactive.png -LyricIcon_P6 = [sing.player6]lyric_active.png -LyricIconD_P6 = [sing.player6]lyric_inactive.png +LyricIcon_P1 = [sing.player]lyric_active.png +LyricIconD_P1 = [sing.player]lyric_inactive.png +LyricIcon_P2 = [sing.player]lyric_active.png +LyricIconD_P2 = [sing.player]lyric_inactive.png +LyricIcon_P3 = [sing.player]lyric_active.png +LyricIconD_P3 = [sing.player]lyric_inactive.png +LyricIcon_P4 = [sing.player]lyric_active.png +LyricIconD_P4 = [sing.player]lyric_inactive.png +LyricIcon_P5 = [sing.player]lyric_active.png +LyricIconD_P5 = [sing.player]lyric_inactive.png +LyricIcon_P6 = [sing.player]lyric_active.png +LyricIconD_P6 = [sing.player]lyric_inactive.png # # # DOWNLOAD SCORE # # # @@ -316,9 +316,9 @@ SelectFrame = [name]select.png AvatarFrame2 = [score]frame.png # # # NO AVATARS # # # -NoAvatar_P1 = [name]noavatarP1.png -NoAvatar_P2 = [name]noavatarP2.png -NoAvatar_P3 = [name]noavatarP3.png -NoAvatar_P4 = [name]noavatarP4.png -NoAvatar_P5 = [name]noavatarP5.png -NoAvatar_P6 = [name]noavatarP6.png +NoAvatar_P1 = [name]noavatar.png +NoAvatar_P2 = [name]noavatar.png +NoAvatar_P3 = [name]noavatar.png +NoAvatar_P4 = [name]noavatar.png +NoAvatar_P5 = [name]noavatar.png +NoAvatar_P6 = [name]noavatar.png diff --git a/game/themes/Modern/[name]noavatar.png b/game/themes/Modern/[name]noavatar.png new file mode 100644 index 0000000000000000000000000000000000000000..00bf0f0745d026573420a07dc1bc172845e5d99b GIT binary patch literal 2550 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 Hu0mjfMi=2I diff --git a/game/themes/Modern/[name]noavatarP10.png b/game/themes/Modern/[name]noavatarP10.png deleted file mode 100644 index 0bed45788b22ed20ce70d82ad9682fd23c87d5f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 973 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 KMNUMnLSTYt;{5gi diff --git a/game/themes/Modern/[name]noavatarP4.png b/game/themes/Modern/[name]noavatarP4.png deleted file mode 100644 index e0b164ea664ba980994a739d11ded9a065e13e6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1015 zcmVYr1ffx9{Z13D}7ZTApex8$Hem)+yTS@{UIBxwKUYy{=jfn+`sQoTx8Oz2kElWiQBqw> z&C|C=FW+4E!Q|Ap$UhKo_3k|3sw!EZ2<(1Xo8~rW|(f%d!(NA@J=v`qL zVr{z52=$?NyB#E@`>0jurrR6ZpRZQk#-G~`keb6m{q=O3LU!g~{?0_7pd_c$NgetO zWtmfZW}$1S%=D@=2i-z(+EzZ=Q;qgK>F5yZ6G}$!pd&k}=somgFA;r&t{kPIPtcc> zBy<6tDWsq)=uK5gKtp@lcC64?iuSy*LNf&&z(nd*=*Woqyl$U1Vje9r8;i=yvdTci~H}tC=#G!Rn7~9cUfG!EqjdAl}f!Wx- za|}1NLo3Oa5si81Bq2ltgy=XXM7smgou?hzpWUF999mP3jOf8J2^&*G+9v1x0bK|-oTlfaJwkK{LNpv&5BkW6hC^#Yhweah=)Znu0&E!?wU{j`{ z7vE(;O(u=^*ut`G-=kw#mv{u7TmlO-RBL*4nx?Qg=>Xb>Qu3zI{6d$oKR?UN1`Meg z(fEFMAQJTVy91G+zn2*r^z$qX9`w_wWFj;u(K#df(SuEez84uT^sUH{p|AI8AVXjO lYotT>MTQUE6&XS_#6PEx@6YP?``t?J>Tm$E+jRVi~9R%#z=O$wo!}DNJ+9RQ-{8h zvTUh6o6t3>%=)Uc1>KV3w5@!!uNv+9($OKQPbe9EA{{wNMW0De&JxjA(v_<;bVmA; zNkSK-Gldj%MS4?J640bQZM#%xEJgeNQlVLc4#-66UFgV&`L?f58!;~y8PB4!vbxB8 zM5kmbsW_U9S*;i?OCo_N|gLP+#IW-n)oW^J<1Die#*@ z)wv=u2xP5hHh*mvKsOB_n)9jxL~~vgI+l+Ya?yI~4401PkmxfannR*hmzh8Xnp@D& z4s9>GFkV_jHW)VF(BFbqSzpeo(A;=K+l3C5p%RB|;1fDAwh=;-2%=MSd$;wEwYqFZ=Q$bA;+q^^(WUv#b?zz4_2`DINwazvH?k?~ z(86z7P?J^jJ&t5qj-SynS(kVLo!mnfX0F!sXvP>>95#ctp_F_mG%x6q?9abt#v_E( zh-i9#I}ix^`Rza;=;ty6gMM6vfrEaSl?;R?NOX>f{vE-FLQh2o3q2MYF!b=A1~ByS mtPu{~7a2TsS7ZRu#Qy+N|KmHbJNowk0000vFO%5C&lH{}z{n5k*c0LUREh0d(vqB8McKt$F`+v{UD4CS)-raF7IZ@Mw?rXpjCv zG|z_c_VGKOGR6|_Fgk-VVL80fCd2ED0ve!Kix6m_b6y_1A56Vg?VJiJE7!Y+@~OsE z{aKlvKM?Qea6qT3W3Uid={~wm$L_%%Jv^szAXX##$RhPpEc>MLP3)li;erjlBpLsQ zncT*}o7^5Tg^pNS%O`X-JLWYiusF>a{RiThba+|bhwYdAbb)R8>0d z|1j*b5gqn1fbN^R&(vrg$1&_p_ZhKnbbEPM#ZRXbXiE1{yU>F3rxU#XoifYH*YWNG zT64LWKcCJr(4Fpl*etpLBPoi)9Qp>vvZnT|LN{PC%d5^BbPI;lw))Y5X><^1N5^13 zv1arMY~-XBeFi%@YeZkcR<7F61=vfW30;BBR9etA*iBt)K!f$P?OdU86deR}g=Psl z0*o}f&Cu)_`U4ShzUB}1>6RiIzo~1AXz9_Ga;1o#T$6CXTGF;Sw=bANaABBUj}8c; zV<3nIkG6xpQbdDCTSCYFK=kOphQ3imOOoh}BAVp+w55Fj^EnaxjtJ7Dokcek(fCcx zzhn&)etn_0Y!l$J35~lFA=$XR-o2S#$I&50^yb|cf+8BRsX0M(xnoBW)=rll`&`hP zI^DNR4xzpzNwRknrRCiWj4>U1Y;EpHj3QmTneoTw4QL?}5iLogO_!O-1X?=K&>rnBxI=002ov JPDHLkV1hjU^(g=V diff --git a/game/themes/Modern/[name]noavatarP7.png b/game/themes/Modern/[name]noavatarP7.png deleted file mode 100644 index 0bed45788b22ed20ce70d82ad9682fd23c87d5f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 973 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 KMNUMnLSTY}^P1uS diff --git a/game/themes/Modern/[sing.player1]lyric_inactive.png b/game/themes/Modern/[sing.player1]lyric_inactive.png deleted file mode 100644 index a349007d9d19627e88ec6fe5023a9bbb95597352..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmV-k1EKthP)O=vX{gr}2mycOY4vRU<$Jt5sm$GLjqU?#({s}r zW~cPNgQcKX_)-O1NwyU>YG2}npqcTO zg<~KhAe{%(1e)23?TRdx?6WYV;mX1!EUk5KKH8`~)vX;1DlsE3o6HpjrDzjCB`a0G z9x18|h=>H1vl9{)>k+KA>>b@n90+5_+B4~uvhqq$L<7Trh$h@ zA_@AD11tW`bJPb9ifovN=JTC`IeA`!2^nU~iiP}T(i%q3<@snR=-XBbtKZB3_}RLK z6S#+b^Z?#84Rq3iFtJ;)l;sh$$3mPrqCMf&Z3e&4zh7uAP1LXAK-y480R8!L&uqLQS^aJ9=a|cziW@3zgM;r|L%3O`NRnS=s5sB o0RI1g=?}lbL#_Tc@J|5-08)I4J{+NUQ~&?~07*qoM6N<$f)|#Ic>n+a diff --git a/game/themes/Modern/[sing.player2]lyric_active.png b/game/themes/Modern/[sing.player2]lyric_active.png deleted file mode 100644 index 509767fa2c4b8a83956495b29ffe7bebdf56de67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1034 zcmV+l1oiugP)Jw4Y(3q2yR@6h9oPEXv|(Q3T~7czuSELVbDmTi^heh zGXe84q^Iq1`u(cB_g44oH{IjJ5oADeAD3IVe&^m(x9X|$mAVVYYf$A7fWm(vE{T(@ z)AvP}dTb18tSo)f zW=nIkb{oszoWboC>u2gXG;Gq}K>F}&--nGN2@$r958)q#VhLWk~j43A-rk3YS~XgaGj z>Tax9n08V}8R*RRYzo*+#=NH9a-X#{X8pPf1yTxYMku4g*|6|t1n3fCODj07VU;F2 zv=pOP-{#NumTC40(`c$juF!uimI{coPAHpz0*Pa|z>r3y0R%fq~`E zzeID(ouUhmJ(2g<_spWcZ*T7Z_T2MmZrhHZ9)2Y6tK2(Hg-68|(-A}oTsIPBOk=6) zP%^7SF8WkBeZSjm4zikPNaT5dU!Re=@xnr({4FeL8o1)Wgt->uw zjlp?kh4I2XOPl_B^oVHA!pscn56%qMrZGB8m!TqhV7mT+(NP7ADH(I4C>gWR51PLI zcw^?FygzNVq@Vk4p5236AKyQ6)~(a0kgP1r9T&9;Y3eiP3vP#iUBTy#AGmaNjZIG? z+`0uj-kV#y^51?7i?1D#S59XhP=NViVPK}r7BcM9xSss};XD>!e+9Sie{vNA7mmFu z_Tuq33-tL6ea4)5Ci@#F!0FJVH1MyTI)NYd?OrngzBYaNp5}DUeYbX`T>{T2a|wW| zn}I!$9iqDEyOHrhWl!Yo>62fLjIaFiGtNxi?VTu+eK=zlFfMX zR;_h$B6uxUus|Y9 zBt$)k;UIV+7*CoQPsG2V2NR=5e}q3quf{}gl1MNyK4Lr=vnY_Dfd!U;%RXkh+p3=K znSFs^Vn_@PwO!pcJzsrQ)wRps-hZfB5CHQ$_*X+q*;GP&QGdGS_cXvT6b*!8ja2HbhQd;wXK(JnDrn+g>t@vYS zpRLXJ{6?M!QiqEKfFMJH<`q~kuP=SeUESS6uw1#|pTO@KA!oh1dR zNikyLVs$J`{Bv11Cd3p}mBjk*?+OaC)DoF8i~yIE3lxkZXGYH);)x@OiHv#I9cF!f z(0}dffmn^wH;7hOjV_8K5L`G9yiiDTPXOphw7w2;xtuuQK)H1QF-77*2QaPK!Ql@l zE}Hupmp36dHI)TX+gRdxe|#KY!OSxzl~hNZr;dnbRD-|^yMXDCxw@4dsD1iubZ>YF z`lc@@Oh-qPCSsp!d@UK4Jl_xb*%^}qK2QKyFJl@g1u6kYLWC(aRvl4q8!lu{w|2F^ zpYGE&Nkc~t;PBE{lyV&Zvo4N&_8{pZ9lF2A8SzizLd08 z?Leh*z&K0{R0pu}3iN?h9c^qteP!9OOe2GZLwht6XgXq*buumNmkct7o}tbdN0n{Z z`}n?h&!(?F``q4#cM$LG_U7ZjcKFK zFCE`|w{!0MF>l84SCEVFT@L5mbLQU9J@=k_M^sLPY6KGFK~fNZX4UUi_L7N8asL2wHRo$L_8(4mvf{V+E{9^uo}_;+cU!kfj{ z==yz~r|Zl#$ZO)@s1t9|p!Z5-)kO(3y)Yz^N6-pvL9dZ1x@-NUc(*;u+ zCq?boZBp0ub#GgUG9aYNibX+b(x`~i6NfITyCA1HZkMCO?1KNW66L$J~7b1X5ZV}E*==WEgTA*W?YGRTmmwG>h$_9k=Y zrN!mzbJe=>ck-?0uMr7U`?mL)HLS*cmSdhTDSsnNKt5y3EkNNbE@Qr=XgKSb(=iJ1 zX(WNTvmm;H9aHV-Kzq^Hx?nv}Y1>cJBS__PFlg}x0bTKmOPa-COZYPO#Scf07P7ID zMdNteKkg_L?x*glM@D5#hibZ&?_DQT7J=>3x4$C<%hj373TPzvgut@w7`N3UKYDDbs2rr zJ!Sq}?8$}xK}bu(rhb}L^i*szd<(n<{_nv2faK|3>6;DyBftPcK&4g#5$w|d0000< KMNUMnLSTa6qu@CJ diff --git a/game/themes/Modern/[sing.player3]lyric_inactive.png b/game/themes/Modern/[sing.player3]lyric_inactive.png deleted file mode 100644 index c5a00600da3ee20512008fae378ad791c19937e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 911 zcmV;A191F_P)5o$cBi3tb4fnGd#^+Wh!^ya~sfER-Y4-oYN0x^XqkU&eVe`wS9XUCo0 zeXsAmmZp%%!LZA|ot@p;-^}dHG_dDipW!sW3PrmJO!j}#eu|b`ckAEsLH^}~&ZF1F z;}d)mO1v~wkf~vT#Vq=QEj!V6*cpAf>_l_J(`!2ALf6^lJGYiPA6~u9znPzJKYq#) z1sq^wB+V@}umpsb1|tCE8dlO7qFF}qGBlerQ+yik=I_DB&eD^`+`CV_C0bwy_Q-Ct znFiNDwz)V1s`t%~q8-$8(JH}=GXphId%@e-21^|8Z6jy}oKY5qfTV;&U4iO3zMily z!3L~f;eY^$#|xGKgcS3o90D@v&0y|QG%7wYJ;F69Af>Zh1{6SjjbMPIZhp={5n z_q9xS(S-)qp)U4~LUBdn7F+#^JIo2!d0EN$=)(>Brm>y?g!BQZcf1T!>JkN>wmh_$ zrGOdhY1hOidwtYv@pEN@yilQKhrUN)U*^D26@YD7c2Wnk-xCE)P_IBD-J} zz>|GnDnoTY5|tkhJ$1KFMHa!hI~LVxi!n{((UupSg8G4&%T)&Mhq6@Td`zX&sKJ$P zThiUMS9Z#MdzUt)8%P^VU2Q}4JekU*3N*>fd4;W lF8^%_WK!Fw4g6Pt0RZytevW&*D@OnT002ovPDHLkV1k`FsS^MI diff --git a/game/themes/Modern/[sing.player4]lyric_active.png b/game/themes/Modern/[sing.player4]lyric_active.png deleted file mode 100644 index 993041fd5b6af98e097c4f6bf074cd7922802e34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 966 zcmV;%13CPOP)LY-cO--u z#U`3F2x6lWf}MqsMxqFkh@!O?DJ(Pw3sI413PrHfMr{%mQ>dL5(Z&dBp}{~jCU}D6 z*W_;RXK#1MH#2*8yLVn<4r3viu*uEFEjm-d5fz@dC%w#U} zY3QY!N?eXTU_)F-zMtR2!cDuEev=Rs1^-nvP6eXrKvWGt;k^`4L#Pjm(BV3kNkDe& z4a_~d$alNcG{7#9__-DqiqtG`b{?*IAgW1#ZeVW!XtF1IxLiEFHTnu>A726H*aua6 zdIIQz{>R5g6!esP$)TzT7MrgFVxZ_Kp$LlXsS)wU5l}X5gJ?@TB!&misLtXHMAC6@!jd&Wdhu&0Cp z--|zU;eVp{#?;esIIfGT3}2+xget;8@xZttHzFXOPJkiU^H~8_NR@fw+5irMb8=Hc zQHW1GuelMzxV$lWuv+4>vLOcRua8yaB?o%Ie9|WeOu~)ULr{C_USM6~ZWq|`k)R<^ z025G4JYMbuqMg0sdeToNTxn>8niDt6gD{r?YMC+eGlB1?1SU2+XZ1P=E zk07qs_n!p~jo{6C)(6b|3CfmTa*Y6+fjj0$E|@$87TusE9iPXns9#9T7vl;gRUd;; zN3RU>uWwA(2U;n2=1Nq)m}_!ei$*)iK}rHu-sD$G#%ioQJxu z{=#_r))BGJUlUw62M*pdxR}4-3ogdN2yt8%1pCGu)_C733!L=F=TO_;EuQDNr$4?( zm~Sh`vcS%!v9!8q5JDYSh$u84ItVk=;HDa;)8-qWE2q=&LzudA5@NS!mreD_)6HPd z#j(cgg%3({DlUSutYrGfDfBGf+gA0rnD6D-mON(5ODiuMmIU+rs8q{GkChkO`UBn2 z&?y7%+toJ6PK|p}iuIk9f`eBKfgvfMU#j_n8iT9kT>+O0amTCli>LDOJ5_#|{zh8^ ouYvzRFfSk$=9T`h!M_9;0Ocfxk#S9gVgLXD07*qoM6N<$f}Hog5&!@I diff --git a/game/themes/Modern/[sing.player4]lyric_inactive.png b/game/themes/Modern/[sing.player4]lyric_inactive.png deleted file mode 100644 index f09669b224a41ad1dce3c4f2e4c9a5606b521148..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 861 zcmV-j1ETziP)wa_y_zEp8Xwq^JK!!=)r>?ghWIS7!sc-Va45LXQ#WY>h5_k z!($Q9R)Ulycr5_}Ub9qAFO}1IX=l=l)UVI8-FMF)4=p~u-MMz>o zghKm-2hA2k{}Mn%jsV#^3E=J$er3=7q>cNI9YL%q$UZy-xX8lqy+=1OmHFpb!cllw zV0)XfHwxpf78-L$M9$ovrA}Gi1Z+cALKaaSqD$9z#Y?_@1ikht2d6(dau(FYtSdL0 z@d4$@&6B0Q4tnK{ z(^GeolXG@0N1A{hST?7Lq;j#{YJsSaI1rN*fVZ<&J2B*aQJ1Z^erIds@}YG=?KT)~ zI+ANE1eAa{F>L7w&A@Rb#@SQ%O7$dYU&HLhAWqMd#GYATl5tpv8nNY^GUQZDu~%c0 zpbRMj1APKEsZE-MKG{`P3FU+>$aEdhQ)5<5eVa__K3-kep0@;Uj0V)DdQS?B4Pu3H zsu3p}P`i4w;*j24x9oV|=PaiVXdNopA^{stFb~X!LgL(VP)ZIvPmEcl7`x;V8~z8|@U@F<~h@Mz#eaaG|#O9@duc(q;Gh zhQuAV_-R>WQFMW7;8Q8AuYKrD++WR>PQ6s5V5)AJk{_8D;Um%&||zG?bi0UE$}l`{n%0!I-l z5CSJa2OV*M`=~hzL)&z~%AN;NoIYy`ydn_0WQ*Ux%TNmeOpqf2I2s~Ao7Tu?LfXPW zjQ||A@P%{;k2ogo;h{ZHRW#^Frk>GN(tGlPF4S|bz!-tzoE#)gm@;C6@3~MU;TfnX zrEskw640RFYiFo7B)ZvponiMw_rTY-aGR@RP%cbDrt1bA?tR?$yK>Th6LUmG zEEt%HzF~CeBTmnROpgdk9jd>E-<6|u2%UGrq^C*(^VrjSmDf;S9+kG49f4f$jI>jS zu0q{?+WgL4I1Pcf&}=548PBP_Gyy@UL}E&E@%F&P-f%7&AEI?54=j0x`S??l1XDnu zfmEcSZjx)G+6rtUXL4c0etf?Kj<$%jTS)A*p4;8e!O9FZ@2IRlgN>i#n2xG?Y90}= zNiIJ~Wu7l^5U}N>Zd}40KfWwG=zqNy~*qp#l4*Gu( zr|Q0ft;!rW{xOx~;uGl%#}u3zEGiykE+P1B1PjnfAvNPc`-pjmPPVx|1;v#~2~=_C zTALa&y-}M7E7gxt<`K9qc|zSQtaBMLY|}F1^9n|`_+@lk2fB|>z>%Z3q`mU(8aDqa zFs!@;l}>m%9e!QsSeF&-0GRr3yIx)`M=U?q_d-g1xa%*Yv%0@ab~k|&Pc0Jv0h0US z_*I;eIqf+y0R}#6^SLq7;*UuvEI&#_)QHOL<2xq!{`})6zJGLXhMj zK2R?ayzIrRARYw4ga1G;{sDi4KSpn!1aFdyf*$mwuQ#hbZ$akfv(@AmCp-M*k|y^4kY(I*$kX&Qm)kWpkDj8 zK#0Ui5Gl7)-?kDYk2fUG1CDZ8?sNx-FQ0UhXLtMN-UEqf0QL9damg8o1eSOpbP7lZ zfDRlH>-^PUFQMVS=U7TN(feOe8EFpq05BLo2DD zr0bBku7c@*c_h*F!7LkF=jH&Mv3p6H0Gcx>y_owx_ZYr)Bn#c%TUO6RUzHwtpOg`4yjN3)K z3t-=-XR5up*Mc|7=JDj3bBU;O7lw31(b{m9#{CtAq6d`?$Jn{na*V*!mroy#_FpVi ztyQ`0-lcA~SWiy=IR!oi{{MjOf2pRw!Stkoe+w`G2smat&#OW%00000NkvXXu0mjf D7OIye diff --git a/game/themes/Modern/[sing.player6]lyric_active.png b/game/themes/Modern/[sing.player6]lyric_active.png deleted file mode 100644 index 65133d034232acec6a606d96140e895cb9a5efd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 960 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 literal 0 HcmV?d00001 diff --git a/game/themes/Modern/[sing.player]lyric_inactive.png b/game/themes/Modern/[sing.player]lyric_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..3a1f8a4a9e25a0786fe34a3d849fa92cd915e699 GIT binary patch literal 1865 zcmbtVdrZ`J94`w4rlLlcfC#RulS$FH@GP106^q6sq7LX(`8F9T>EziEeI%BPV1J zKG`vkVW=EI`2Bv|&)|wyMo>1JjUZ`)rY#U*(W_*G4_IV<&Nzbu=z=DxhNQ@-$;g)~ z6^0#y-j0RgQ^T~f9+C=*Mg(}3pm5R*D+m;YFix$|yg}olKmafB0ol+YmI`B4k76jg zNBJA-@aIPiKzW_cu#E|Q@%h3gbYp2H3?n4SglOGWr2=9F(3J{J081;On{&))R4!Ws zyrF0=Me&X&t8m<9l+M7d=scL4bdshraUagGG=l=h&9W3oMbUYyBw1Aadg?=5z`+~9 zj+vV*Bx50I7n#A)3}>~?Cn=63$DmF{l-yMlLNhoj;}IyVD3Le#e+7#I=TJ5AxePOq0pvUE7?cB-B#~pRB+E0Uq$R^`<1IEf zOIdh|Wh|oA%JXg;Yb&L~`yGlUT6$~+#%TQ>07?HL-D5CaaT|VK1B=U`TK}ig2pAV( zcJEk-#9yNietW3X$Kh~%n9K0&f)C;82V^+%G&tMTny7vRiRjLEEOzDX8gfzVKQ5f| z`>AVlsdTZsKeZ%lB*ng{di&wy8Qov6eIt4vC7z!V6Dgb&MzOf}>)sm9e5Eye{`9-6 z?u^_y7GK?S?W+rJhG{P7UA}ttfsxIfqcOGLfwtna{FKJ$s287Z?zmC4cKE~LE!7_+ zZBd$1E)P}SdwbxzweR*0*4fhaQhD8L2?Mt1FOjWiVat!118F_W4*$^b)5wG5FWT9a zE1QuL2EVu{;z%_1dS-(&C5~z7>#aPqDJt^QW7LsE{leY_wX>)0h*J@AL(;3gZarE! zH|V%@YH0Z9UvI797Ddl|f;t_Q6g#W`sgD2eyfOC&z5dxslr1_Dp2X+^Iu{x8B}$GiQ*^xT}7#$8bJZtdA;aa3+fA3R|@xxc=T(o?I?J9gF#)a+QEbD zmG+K%^~VP)Hl6CX7PWu2U|myZ+b^SC@6n^~ypR4!QE$)cZqW{HJ|{ic9dG{Z@|PAk JPAn;@{S!GKhkF13 literal 0 HcmV?d00001 From e867e91d4e4206e58784ca50d010837b7cde0ffd Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:21:04 +0200 Subject: [PATCH 02/11] Add generic Modern player templates --- game/themes/Modern.ini | 405 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) diff --git a/game/themes/Modern.ini b/game/themes/Modern.ini index 5bcbe16b4..5288da034 100644 --- a/game/themes/Modern.ini +++ b/game/themes/Modern.ini @@ -468,6 +468,114 @@ H = 8 # O N E P L A Y E R M O D E # # # # # # # # # # # # # # # # # # # # #PlayerOne +[SingPlayerTemplateStatic] +Inherits = SingDefaultAvatarFrame +Color = P1Dark + +[SingPlayerTemplateText] +Inherits = SingDefaultPlayerText + +[SingPlayerTemplateStatic2] +Inherits = SingDefaultScoreBG +Color = P1Dark + +[SingPlayerTemplateTextScore] +Inherits = SingDefaultPlayerTextScore + +[SingPlayerTemplateSingBar] +Inherits = SingDefaultSingBar + +[SingPlayerTemplateAvatar] +Inherits = SingDefaultAvatar + +[SingPlayerGrid] +# Left edge of the sing lane area. +LaneAreaX = 20 +# Top edge of the sing lane area. +LaneAreaY = 110 +# Top edge of the sing lane area when top lyrics space is not reserved. +LaneAreaYNoLyrics = 50 +# Width of the sing lane area shared by all player columns. +LaneAreaW = 760 +# Lane area height when top lyrics space is reserved (duet/reserved layout). +LaneAreaHReserved = 365 +# Lane area height when top lyrics space is not reserved. +LaneAreaHNoLyrics = 425 +# Horizontal gap between player columns. +LaneColumnGap = 20 +# Additional vertical gap between player rows. +LaneRowGap = 20 +# Extra inset applied to the note grid to the left of the lane column. +LaneGridLeftInset = 0 +# Base note-line spacing reference for vertical scaling. +LaneBaseLineSpacing = 15 +# Number of horizontal guide lines drawn per slot. +LaneGuideLineCount = 9 +# Guide-line index used as the note row anchor (0-based from the top). +LaneRowAnchorGuideIndex = 7 +# Reference lane width for widget/note scaling calculations. +WidgetScaleReferenceW = 300 +# Per-player widget scale reduction factor. +WidgetScalePerPlayer = 0.02 +# Minimum widget scale factor. +WidgetScaleMin = 0.82 + +[SingPlayerWidgetPlacement] +# Minimum avatar frame width after scaling. +MinFrameW = 26 +# Minimum avatar frame height after scaling. +MinFrameH = 26 +# Minimum score box width after scaling. +MinScoreW = 56 +# Minimum score box height after scaling. +MinScoreH = 18 +# Horizontal offset from lane left edge to header group start. +HeaderLeftOffset = 0 +# Vertical offset from row anchor to header group. +HeaderTopOffsetBase = 148 +# Extra header vertical offset per additional row. +HeaderTopOffsetPerExtraRow = 0 +# Minimum inset of avatar texture inside its frame (X). +AvatarInsetMinX = 1 +# Minimum inset of avatar texture inside its frame (Y). +AvatarInsetMinY = 1 +# Base horizontal gap between avatar frame and player name. +NameGapX = 10 +# Minimum horizontal gap between avatar frame and player name. +NameGapMinX = 8 +# Base horizontal text padding for player name. +NamePaddingX = 6 +# Minimum horizontal text padding for player name. +NamePaddingMinX = 4 +# Base vertical text padding for player name. +NamePaddingY = 6 +# Minimum vertical text padding for player name. +NamePaddingMinY = 3 +# Minimum width allocated to player name text. +NameMinW = 24 +# Minimum height allocated to player name text. +NameMinH = 14 +# Minimum font size for player name text. +NameMinSize = 10 +# Max fraction of lane width reserved for score box. +ScoreWidthFractionOfLane = 0.36 +# Base vertical gap between player name and oscilloscope. +OscilloscopeGapY = 4 +# Minimum vertical gap between player name and oscilloscope. +OscilloscopeGapMinY = 2 +# Minimum oscilloscope width. +OscilloscopeMinW = 40 +# Minimum oscilloscope height. +OscilloscopeMinH = 8 +# Solo mode popup Y offset. +PopupYOffsetSolo = 65 +# Duet mode popup Y offset. +PopupYOffsetDuet = 40 +# Solo mode popup font size. +PopupFontSizeSolo = 18 +# Duet mode popup font size. +PopupFontSizeDuet = 14 + [SingP1Static] Inherits = SingDefaultAvatarFrame Color = P1Dark @@ -685,6 +793,242 @@ Text = SONG_SCORE_WHEREAMI #end of main stuff # # # # # # # # # # # # # # # # # # One Player Score # # # # # # # # # # # # +[ScorePlayerSlotArea] +AreaX = 20 +AreaY = 110 +AreaW = 760 +AreaH = 420 +ExtraColsBias = 1 + +# Score player template used for all dynamic score screen layouts. +[ScorePlayerTemplateTextName] +X = 197 +Y = 280 +Font = 0 +Style = 1 +Size = 40 +Text = P1 +Color = White +Align = 0 + +# Rating +[ScorePlayerTemplateStaticRatingPicture] +X = 385 +Y = 165 +H = 75 +W = 75 + +[ScorePlayerTemplateTextScore] +X = 422 +Y = 235 +W = 100 +Color = White +Font = 0 +Style = 0 +Size = 27 +Text = Tone Deaf +Align = 1 + +# Note Score +[ScorePlayerTemplateStaticBoxDark] +Tex = ScoreBar_box_dark +X = 200 +Y = 327 +W = 22 +H = 20 +Color = P1Dark +Type = Colorized + +[ScorePlayerTemplateTextNotes] +X = 227 +Y = 322 +Color = White +Font = 0 +Style = 0 +Size = 30 +Text = SING_NOTES +Align = 0 + +[ScorePlayerTemplateTextNotesScore] +X = 487 +Y = 322 +Color = White +Font = 0 +Style = 0 +Size = 30 +Align = 2 +Text = 0 + +# A simple line +[ScorePlayerTemplateStatic1] +Tex = ScoreLine +X = 200 +Y = 351 +W = 287 +H = 1 +Color = White +Type = Colorized + +# Line Bonus +[ScorePlayerTemplateStaticBoxLight] +Inherits = ScorePlayerTemplateStaticBoxDark +Tex = ScoreBar_box_light +Y = 358 +Color = P1Light + +[ScorePlayerTemplateTextLineBonus] +Inherits = ScorePlayerTemplateTextNotes +Y = 352 +Text = SING_PHRASE_BONUS + +[ScorePlayerTemplateTextLineBonusScore] +Inherits = ScorePlayerTemplateTextNotesScore +Y = 352 + +# A simple line +[ScorePlayerTemplateStatic2] +Inherits = ScorePlayerTemplateStatic1 +Y = 382 + +# Golden Notes +[ScorePlayerTemplateStaticBoxLightest] +Inherits = ScorePlayerTemplateStaticBoxDark +Tex = ScoreBar_box_lightest +Y = 390 +Color = P1Lightest + +[ScorePlayerTemplateTextGoldenNotes] +Inherits = ScorePlayerTemplateTextNotes +Y = 383 +Text = SING_GOLDEN_NOTES + +[ScorePlayerTemplateTextGoldenNotesScore] +Inherits = ScorePlayerTemplateTextNotesScore +Y = 383 + +[ScorePlayerTemplateTextTotal] +X = 237 +Y = 454 +Color = White +Font = 0 +Style = 0 +Size = 30 +Text = SING_TOTAL +Align = 0 +Reflection = 1 +ReflectionSpacing = 26 + +[ScorePlayerTemplateTextTotalScore] +X = 487 +Y = 444 +Color = White +Font = 0 +Style = 0 +Size = 42 +Align = 2 +Text = 0 +Reflection = 1 +ReflectionSpacing = 24 + +#ScoreBar +[ScorePlayerTemplateStaticBackLevel] +Tex = ScoreLevel +X = 503 +Y = 168 +W = 95 +H = 310 +Color = P1Lightest +Type = Colorized + +[ScorePlayerTemplateStaticBackLevelRound] +Tex = ScoreLevelRound +X = 503 +Y = 138 +W = 95 +H = 8 +Color = P1Lightest +Type = Colorized + +[ScorePlayerTemplateStaticLevel] +Tex = ScoreLevel +X = 503 +Y = 400 +W = 95 +H = 10 +Color = P1Dark +Type = Colorized + +[ScorePlayerTemplateStaticLevelRound] +Tex = ScoreLevelRound +X = 503 +Y = 392 +W = 95 +H = 8 +Color = P1Dark +Type = Colorized + +[ScorePlayerTemplateStatic3] +Tex = ScoreEndCap +X = 499 +Y = 478 +W = 110 +H = 30 +z = 0.9 +Color = P1Dark +Type = Colorized +Reflection = 1 +ReflectionSpacing = 0 + +[ScorePlayerTemplateStatic4] +Tex = ScoreGlassBox +X = 499 +Y = 148 +W = 113 +H = 331 +z = 0.89 +Color = White +Type = Transparent + +[ScorePlayerTemplateStatic5] +Tex = P +X = 200 +Y = 455 +W = 26 +H = 23 +Color = P1Dark +Type = Colorized +Reflection = 1 +ReflectionSpacing = 31 + +[ScorePlayerTemplateText1] +Text = P1 +X = 204 +Y = 458 +Size = 18 +Font = 0 +Style = 1 +Color = White +Type = Colorized +Reflection = 1 +ReflectionSpacing = 40 + +[ScorePlayerTemplateAvatar] +X = 203 +Y = 168 +W = 104 +H = 104 +Z = 1 + +[ScorePlayerTemplateStatic6] +X = 195 +Y = 160 +W = 120 +H = 120 +Z = 0.9 +Tex = AvatarFrame2 +Color = P1Dark +Type = Transparent + [ScoreTextName1] X = 197 Y = 280 @@ -2743,6 +3087,45 @@ H = 35 SkipX = 10 SBGW = 360 +[NamePlayerSelectTemplateFrame] +X = 100 +Y = 185 +W = 70 +H = 60 +Tex = AvatarFrame2 +Color = P1Dark +Type = Transparent +Z = 1 + +[NamePlayerSelectTemplateText] +X = 135 +Y = 248 +Font = 0 +Style = 1 +Size = 16 +Align = 1 +Color = White +Z = 1 + +[NamePlayerSelectTemplateAvatar] +X = 105 +Y = 190 +W = 60 +H = 51 +Z = 1 + +[NamePlayerSelectGrid] +GridX = 30 +GridY = 185 +GridW = 760 +GridH = 155 +MinimumVisibleSlots = 2 +MaximumColumns = 12 +RowVerticalSpreadDivisor = 1.5 +MaxScaleUpTo2Players = 1.35 +MaxScaleUpTo4Players = 1.15 +MaxScaleDefault = 1.0 + [NamePlayerSelectStatic1] X = 100 Y = 185 @@ -6511,6 +6894,12 @@ Size = 20 Align = 1 Color = White +[SongRouletteDuetSingerArea] +X = 530 +Y = 340 + +############################# + [SongRouletteStatic2PlayersDuetSingerP2] Inherits = SongRouletteStatic2PlayersDuetSingerP1 Y = 445 @@ -6586,6 +6975,10 @@ Inherits = SongRouletteText2PlayersDuetSingerP1 X = 630 Y = 245 +[SongListDuetSingerArea] +X = 525 +Y = 110 + [SongListStatic2PlayersDuetSingerP2] Inherits = SongListStatic2PlayersDuetSingerP1 Y = 270 @@ -6662,6 +7055,10 @@ Inherits = SongRouletteText2PlayersDuetSingerP1 X = 387 Y = 440 +[SongChessboardDuetSingerArea] +X = 450 +Y = 420 + [SongChessboardStatic2PlayersDuetSingerP2] Inherits = SongChessboardStatic2PlayersDuetSingerP1 Y = 465 @@ -9721,6 +10118,14 @@ Y = 185 # O N E P L A Y E R M O D E # # # # # # # # # # # # # # # # # # # # #PlayerOne +[SingPlayerTemplateOscilloscope] +X = 80 +Y = 275 +H = 30 +W = 100 + +## 4/6 Players One Screen ## + [SingP1Oscilloscope] X = 80 Y = 275 From d5f46af6e002cd79db2cea5a9b4b6c07be33d1f8 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:21:19 +0200 Subject: [PATCH 03/11] Add player layout helper --- src/base/UPlayerLayout.pas | 515 +++++++++++++++++++++++++++++++++++++ src/ultrastardx-unix.lpi | 5 + src/ultrastardx-win.lpi | 4 + src/ultrastardx.dpr | 1 + 4 files changed, 525 insertions(+) create mode 100644 src/base/UPlayerLayout.pas diff --git a/src/base/UPlayerLayout.pas b/src/base/UPlayerLayout.pas new file mode 100644 index 000000000..adab48469 --- /dev/null +++ b/src/base/UPlayerLayout.pas @@ -0,0 +1,515 @@ +{* + * Shared player-count layout helpers. + *} + +unit UPlayerLayout; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +uses + Math; + +type + TPlayerGrid = record + Cols: integer; + Rows: integer; + end; + + TPlayerSlotRect = record + X: integer; + Y: integer; + W: integer; + H: integer; + end; + + TSingPlayerLayoutConfig = record + ColumnContainerLeft: integer; + ColumnContainerTopReserved: integer; + ColumnContainerTopNoLyrics: integer; + ColumnContainerWidth: integer; + LaneHeightReserved: integer; + LaneHeightNoLyrics: integer; + ColumnGap: integer; + RowGap: integer; + GridExtraLeft: integer; + BaseLineSpacing: integer; + GuideLineCount: integer; + RowAnchorGuideIndex: integer; + WidgetScaleBaseWidth: integer; + WidgetScalePerPlayer: real; + WidgetScaleMin: real; + end; + + TSingLaneLayout = record + GridCols: integer; + GridRows: integer; + ColumnLeft: integer; + ColumnRight: integer; + ColumnWidth: integer; + GridLeft: integer; + GridRight: integer; + GridWidth: integer; + RowAnchorY: integer; + GuideTopY: integer; + NoteLineSpacing: integer; + SlotScale: real; + ContentScale: real; + WidgetScale: real; + end; + +function GetPlayerGrid(PlayerCount: integer; Flip: boolean = false): TPlayerGrid; +function GetPlayerGridForArea(PlayerCount, AreaW, AreaH: integer; SmallCountWide: boolean): TPlayerGrid; +function GetScorePlayerGrid(PlayerCount, AreaW, AreaH, ExtraColsBias: integer): TPlayerGrid; +function GetSingPlayerGrid(PlayerCountOnScreen: integer; const Config: TSingPlayerLayoutConfig; + ReserveTopLyricsSpace: boolean = true): TPlayerGrid; +procedure GetPlayerColumnLayout(ColIndex, ColCount, ContainerLeft, ContainerWidth, ColumnGap: integer; + out LaneLeft, LaneRight, LaneWidth: integer); +function GetPlayerWidgetScale(PlayerCount: integer; const Config: TSingPlayerLayoutConfig): real; +function GetSingLaneLayout(PlayerCountOnScreen, PlayerIndexOnScreen: integer; + const Config: TSingPlayerLayoutConfig; ReserveTopLyricsSpace: boolean = true): TSingLaneLayout; +function GetScreenPlayerCount(PlayerCount, ScreenCount, ScreenIndex: integer): integer; +function GetFirstPlayerIndexForScreen(PlayerCount, ScreenCount, ScreenIndex: integer): integer; +function GetPlayerScreen(PlayerIndex, PlayerCount, ScreenCount: integer): integer; +function GetPlayerIndexOnScreen(PlayerIndex, PlayerCount, ScreenCount: integer): integer; +function GetPlayerSlotRect(PlayerIndexOnScreen, PlayerCountOnScreen: integer; + ContainerX, ContainerY, ContainerW, ContainerH: integer; Flip: boolean = false): TPlayerSlotRect; overload; +function GetPlayerSlotRect(PlayerIndexOnScreen: integer; const Grid: TPlayerGrid; + ContainerX, ContainerY, ContainerW, ContainerH: integer): TPlayerSlotRect; overload; +function GetScaledGridLayoutBounds(const BaseBounds, AreaBounds: TPlayerSlotRect; + const Grid: TPlayerGrid): TPlayerSlotRect; overload; +function GetScaledGridLayoutBounds(const BaseBounds, AreaBounds: TPlayerSlotRect; + PlayerCount: integer; Wide: boolean): TPlayerSlotRect; overload; +function ScaleCoordToSlot(const Value, SourceStart, SourceSize, TargetStart, TargetSize: integer): integer; +function ScaleLengthToSlot(const Value, SourceSize, TargetSize: integer): integer; +function GetFittedSlotRect(const SourceBounds, SlotRect: TPlayerSlotRect; MaxScale: real): TPlayerSlotRect; + +implementation + +function GetGridAspectScore(const Grid: TPlayerGrid; AreaW, AreaH: integer): real; +var + AreaAspect: real; + GridAspect: real; +begin + if (Grid.Cols <= 0) or (Grid.Rows <= 0) or (AreaW <= 0) or (AreaH <= 0) then + Exit(0); + + AreaAspect := AreaW / Max(1.0, AreaH); + GridAspect := Grid.Cols / Max(1.0, Grid.Rows); + Result := Min(AreaAspect, GridAspect) / Max(AreaAspect, GridAspect); +end; + +function GetGridSlotSquareScore(const Grid: TPlayerGrid; AreaW, AreaH: integer): real; +var + SlotAspect: real; +begin + if (Grid.Cols <= 0) or (Grid.Rows <= 0) or (AreaW <= 0) or (AreaH <= 0) then + Exit(0); + + SlotAspect := (AreaW / Max(1.0, Grid.Cols)) / (AreaH / Max(1.0, Grid.Rows)); + Result := Min(SlotAspect, 1.0 / Max(0.0001, SlotAspect)); +end; + +function GetGridSlotMinSize(const Grid: TPlayerGrid; AreaW, AreaH: integer): real; +begin + if (Grid.Cols <= 0) or (Grid.Rows <= 0) or (AreaW <= 0) or (AreaH <= 0) then + Exit(0); + + Result := Min(AreaW / Max(1.0, Grid.Cols), AreaH / Max(1.0, Grid.Rows)); +end; + +function GetGridUnusedSlots(const Grid: TPlayerGrid; PlayerCount: integer): integer; +begin + Result := Max(0, Grid.Cols * Grid.Rows - PlayerCount); +end; + +function GetPlayerGrid(PlayerCount: integer; Flip: boolean = false): TPlayerGrid; +var + temp: integer; +begin + if PlayerCount <= 3 then + begin + Result.Cols := PlayerCount; + Result.Rows := 1; + end + else + begin + Result.Rows := Trunc(Ceil(Sqrt(PlayerCount))); + Result.Cols := Trunc(Ceil(PlayerCount / Result.Rows)); + end; + if Flip then + begin + temp := Result.Cols; + Result.Cols := Result.Rows; + Result.Rows := temp; + end; +end; + +function GetPlayerGridForArea(PlayerCount, AreaW, AreaH: integer; SmallCountWide: boolean): TPlayerGrid; +var + TallGrid: TPlayerGrid; + WideGrid: TPlayerGrid; +begin + if PlayerCount <= 3 then + begin + if SmallCountWide then + begin + Result.Cols := PlayerCount; + Result.Rows := 1; + end + else + begin + Result.Cols := 1; + Result.Rows := PlayerCount; + end; + end + else + begin + TallGrid := GetPlayerGrid(PlayerCount, false); + WideGrid := GetPlayerGrid(PlayerCount, true); + + if GetGridAspectScore(WideGrid, AreaW, AreaH) >= GetGridAspectScore(TallGrid, AreaW, AreaH) then + Result := WideGrid + else + Result := TallGrid; + end; +end; + +function GetScorePlayerGrid(PlayerCount, AreaW, AreaH, ExtraColsBias: integer): TPlayerGrid; +var + BiasedGrid: TPlayerGrid; + BaseSquareScore: real; + BiasedSquareScore: real; + BaseMinSize: real; + BiasedMinSize: real; + BaseUnusedSlots: integer; + BiasedUnusedSlots: integer; +begin + Result := GetPlayerGridForArea(PlayerCount, AreaW, AreaH, true); + + if (PlayerCount <= 3) or (ExtraColsBias <= 0) then + Exit; + + BiasedGrid := Result; + BiasedGrid.Cols := Min(PlayerCount, Result.Cols + ExtraColsBias); + BiasedGrid.Rows := Trunc(Ceil(PlayerCount / Max(1.0, BiasedGrid.Cols))); + + if (BiasedGrid.Cols = Result.Cols) and (BiasedGrid.Rows = Result.Rows) then + Exit; + + BaseSquareScore := GetGridSlotSquareScore(Result, AreaW, AreaH); + BiasedSquareScore := GetGridSlotSquareScore(BiasedGrid, AreaW, AreaH); + BaseMinSize := GetGridSlotMinSize(Result, AreaW, AreaH); + BiasedMinSize := GetGridSlotMinSize(BiasedGrid, AreaW, AreaH); + BaseUnusedSlots := GetGridUnusedSlots(Result, PlayerCount); + BiasedUnusedSlots := GetGridUnusedSlots(BiasedGrid, PlayerCount); + + if ((BiasedSquareScore > BaseSquareScore) and (BiasedUnusedSlots <= BaseUnusedSlots + 1)) or + ((BiasedUnusedSlots < BaseUnusedSlots) and (BiasedMinSize > BaseMinSize) and + (BiasedSquareScore >= BaseSquareScore - 0.05)) then + Result := BiasedGrid; +end; + +function GetSingPlayerGrid(PlayerCountOnScreen: integer; const Config: TSingPlayerLayoutConfig; + ReserveTopLyricsSpace: boolean = true): TPlayerGrid; +var + BaseGrid: TPlayerGrid; + TallGrid: TPlayerGrid; + LayoutHeight: integer; +begin + if ReserveTopLyricsSpace then + LayoutHeight := Max(1, Config.LaneHeightReserved) + else + LayoutHeight := Max(1, Config.LaneHeightNoLyrics); + + BaseGrid := GetPlayerGridForArea(PlayerCountOnScreen, Config.ColumnContainerWidth, LayoutHeight, false); + + if (not ReserveTopLyricsSpace) and (PlayerCountOnScreen > 3) then + begin + TallGrid := GetPlayerGrid(PlayerCountOnScreen, false); + if (TallGrid.Rows > BaseGrid.Rows) then + Result := TallGrid + else + Result := BaseGrid; + end + else + Result := BaseGrid; +end; + +procedure GetPlayerColumnLayout(ColIndex, ColCount, ContainerLeft, ContainerWidth, ColumnGap: integer; + out LaneLeft, LaneRight, LaneWidth: integer); +var + ColumnWidth: integer; +begin + LaneLeft := ContainerLeft; + LaneWidth := ContainerWidth; + LaneRight := ContainerLeft + ContainerWidth; + + if ColCount <= 1 then + Exit; + + ColumnWidth := Round((ContainerWidth - (ColumnGap * (ColCount - 1))) / ColCount); + LaneLeft := ContainerLeft + ColIndex * (ColumnWidth + ColumnGap); + LaneWidth := ColumnWidth; + LaneRight := LaneLeft + ColumnWidth; +end; + +function GetPlayerWidgetScale(PlayerCount: integer; const Config: TSingPlayerLayoutConfig): real; +begin + Result := 1.0 - Max(0, PlayerCount - 1) * Config.WidgetScalePerPlayer; + if Result < Config.WidgetScaleMin then + Result := Config.WidgetScaleMin; +end; + +function GetSingLaneLayout(PlayerCountOnScreen, PlayerIndexOnScreen: integer; + const Config: TSingPlayerLayoutConfig; ReserveTopLyricsSpace: boolean = true): TSingLaneLayout; +var + Grid: TPlayerGrid; + ColIndex: integer; + RowIndex: integer; + AreaTop: integer; + AreaHeight: integer; + RowGap: integer; + SlotTopY: integer; + SlotBottomY: integer; + SlotHeight: integer; + TopPadding: integer; + MaxLineSpacing: integer; + GuideLineCount: integer; + RowAnchorGuideIndex: integer; + ColumnScale: real; + AvailableRowScale: real; + BaseLineSpacing: integer; + GridInsetX: integer; +begin + Grid := GetSingPlayerGrid(PlayerCountOnScreen, Config, ReserveTopLyricsSpace); + Result.GridCols := Grid.Cols; + Result.GridRows := Grid.Rows; + ColIndex := PlayerIndexOnScreen div Grid.Rows; + RowIndex := PlayerIndexOnScreen mod Grid.Rows; + + if ReserveTopLyricsSpace then + begin + AreaTop := Config.ColumnContainerTopReserved; + AreaHeight := Max(1, Config.LaneHeightReserved) + end + else + begin + AreaTop := Config.ColumnContainerTopNoLyrics; + AreaHeight := Max(1, Config.LaneHeightNoLyrics); + end; + RowGap := Max(0, Config.RowGap); + if Grid.Rows > 1 then + SlotTopY := AreaTop + RowIndex * RowGap + + Round(RowIndex * Max(1.0, AreaHeight - RowGap * (Grid.Rows - 1)) / Grid.Rows) + else + SlotTopY := AreaTop; + if Grid.Rows > 1 then + SlotBottomY := AreaTop + RowIndex * RowGap + + Round((RowIndex + 1) * Max(1.0, AreaHeight - RowGap * (Grid.Rows - 1)) / Grid.Rows) + else + SlotBottomY := AreaTop + AreaHeight; + SlotHeight := Max(1, SlotBottomY - SlotTopY); + + GetPlayerColumnLayout(ColIndex, Grid.Cols, Config.ColumnContainerLeft, Config.ColumnContainerWidth, + Max(0, Config.ColumnGap), Result.ColumnLeft, Result.ColumnRight, Result.ColumnWidth); + GuideLineCount := Max(2, Config.GuideLineCount); + RowAnchorGuideIndex := EnsureRange(Config.RowAnchorGuideIndex, 0, GuideLineCount - 1); + BaseLineSpacing := Max(1, Config.BaseLineSpacing); + ColumnScale := Result.ColumnWidth / Max(1.0, Config.WidgetScaleBaseWidth); + MaxLineSpacing := Max(6, SlotHeight div GuideLineCount); + AvailableRowScale := MaxLineSpacing / Max(1.0, BaseLineSpacing); + Result.SlotScale := Min(1.0, Min(ColumnScale, AvailableRowScale)); + Result.ContentScale := Result.SlotScale * GetPlayerWidgetScale(PlayerCountOnScreen, Config); + Result.NoteLineSpacing := Max(6, Min(MaxLineSpacing, Round(BaseLineSpacing * Result.ContentScale))); + TopPadding := Max(0, SlotHeight - GuideLineCount * Result.NoteLineSpacing); + Result.GuideTopY := SlotTopY + (TopPadding div 2); + Result.RowAnchorY := Result.GuideTopY + RowAnchorGuideIndex * Result.NoteLineSpacing; + GridInsetX := Max(1, Round(Config.GridExtraLeft * Result.ContentScale)); + Result.GridLeft := Result.ColumnLeft - GridInsetX; + Result.GridRight := Result.ColumnRight; + Result.GridWidth := Result.GridRight - Result.GridLeft; + Result.WidgetScale := Result.ContentScale; +end; + +function GetScreenPlayerCount(PlayerCount, ScreenCount, ScreenIndex: integer): integer; +var + BaseCount: integer; + Remainder: integer; +begin + if (PlayerCount <= 0) or (ScreenCount <= 0) or (ScreenIndex <= 0) or (ScreenIndex > ScreenCount) then + Exit(0); + + BaseCount := PlayerCount div ScreenCount; + Remainder := PlayerCount mod ScreenCount; + + Result := BaseCount; + if ScreenIndex <= Remainder then + Inc(Result); +end; + +function GetFirstPlayerIndexForScreen(PlayerCount, ScreenCount, ScreenIndex: integer): integer; +var + CurrentScreen: integer; +begin + if (PlayerCount <= 0) or (ScreenCount <= 0) or (ScreenIndex <= 0) or (ScreenIndex > ScreenCount) then + Exit(0); + + Result := 0; + for CurrentScreen := 1 to ScreenIndex - 1 do + Inc(Result, GetScreenPlayerCount(PlayerCount, ScreenCount, CurrentScreen)); +end; + +function GetPlayerScreen(PlayerIndex, PlayerCount, ScreenCount: integer): integer; +var + CurrentScreen: integer; + RemainingPlayers: integer; + PlayersOnScreen: integer; +begin + if (PlayerIndex < 0) or (PlayerIndex >= PlayerCount) or (ScreenCount <= 0) then + Exit(0); + + RemainingPlayers := PlayerIndex; + for CurrentScreen := 1 to ScreenCount do + begin + PlayersOnScreen := GetScreenPlayerCount(PlayerCount, ScreenCount, CurrentScreen); + if RemainingPlayers < PlayersOnScreen then + Exit(CurrentScreen); + Dec(RemainingPlayers, PlayersOnScreen); + end; + + Result := 0; +end; + +function GetPlayerIndexOnScreen(PlayerIndex, PlayerCount, ScreenCount: integer): integer; +var + CurrentScreen: integer; + TargetScreen: integer; +begin + Result := PlayerIndex; + TargetScreen := GetPlayerScreen(PlayerIndex, PlayerCount, ScreenCount); + + for CurrentScreen := 1 to TargetScreen - 1 do + Dec(Result, GetScreenPlayerCount(PlayerCount, ScreenCount, CurrentScreen)); +end; + +function GetPlayerSlotRect(PlayerIndexOnScreen, PlayerCountOnScreen: integer; + ContainerX, ContainerY, ContainerW, ContainerH: integer; Flip: boolean = false): TPlayerSlotRect; +var + Grid: TPlayerGrid; +begin + Grid := GetPlayerGrid(PlayerCountOnScreen, Flip); + Result := GetPlayerSlotRect(PlayerIndexOnScreen, Grid, ContainerX, ContainerY, ContainerW, ContainerH); +end; + +function GetPlayerSlotRect(PlayerIndexOnScreen: integer; const Grid: TPlayerGrid; + ContainerX, ContainerY, ContainerW, ContainerH: integer): TPlayerSlotRect; +var + ColIndex: integer; + RowIndex: integer; + ColWidth: integer; + RowHeight: integer; +begin + if (PlayerIndexOnScreen < 0) or (PlayerIndexOnScreen >= Grid.Cols * Grid.Rows) or + (ContainerW <= 0) or (ContainerH <= 0) then + begin + Result.X := ContainerX; + Result.Y := ContainerY; + Result.W := 0; + Result.H := 0; + Exit; + end; + + if (Grid.Cols <= 0) or (Grid.Rows <= 0) then + begin + Result.X := ContainerX; + Result.Y := ContainerY; + Result.W := 0; + Result.H := 0; + Exit; + end; + + ColIndex := PlayerIndexOnScreen div Grid.Rows; + RowIndex := PlayerIndexOnScreen mod Grid.Rows; + ColWidth := ContainerW div Grid.Cols; + RowHeight := ContainerH div Grid.Rows; + + Result.X := ContainerX + (ColIndex * ColWidth); + Result.Y := ContainerY + (RowIndex * RowHeight); + + if ColIndex = Grid.Cols - 1 then + Result.W := ContainerX + ContainerW - Result.X + else + Result.W := ColWidth; + + if RowIndex = Grid.Rows - 1 then + Result.H := ContainerY + ContainerH - Result.Y + else + Result.H := RowHeight; +end; + +function GetScaledGridLayoutBounds(const BaseBounds, AreaBounds: TPlayerSlotRect; + const Grid: TPlayerGrid): TPlayerSlotRect; +var + Scale: real; + ScaleX: real; + ScaleY: real; +begin + if (Grid.Cols <= 0) or (Grid.Rows <= 0) or (BaseBounds.W <= 0) or (BaseBounds.H <= 0) then + Exit(BaseBounds); + + ScaleX := AreaBounds.W / Max(1.0, BaseBounds.W * Grid.Cols); + ScaleY := AreaBounds.H / Max(1.0, BaseBounds.H * Grid.Rows); + Scale := Min(1.0, Min(ScaleX, ScaleY)); + + Result.W := Round(BaseBounds.W * Grid.Cols * Scale); + Result.H := Round(BaseBounds.H * Grid.Rows * Scale); + Result.X := AreaBounds.X + Max(0, (AreaBounds.W - Result.W) div 2); + Result.Y := AreaBounds.Y + Max(0, (AreaBounds.H - Result.H) div 2); +end; + +function GetScaledGridLayoutBounds(const BaseBounds, AreaBounds: TPlayerSlotRect; + PlayerCount: integer; Wide: boolean): TPlayerSlotRect; +var + Grid: TPlayerGrid; +begin + Grid := GetPlayerGridForArea(PlayerCount, AreaBounds.W, AreaBounds.H, Wide); + Result := GetScaledGridLayoutBounds(BaseBounds, AreaBounds, Grid); +end; + +function ScaleCoordToSlot(const Value, SourceStart, SourceSize, TargetStart, TargetSize: integer): integer; +begin + if SourceSize <= 0 then + Exit(TargetStart); + + Result := TargetStart + Round((Value - SourceStart) * TargetSize / SourceSize); +end; + +function ScaleLengthToSlot(const Value, SourceSize, TargetSize: integer): integer; +begin + if SourceSize <= 0 then + Exit(Value); + + Result := Round(Value * TargetSize / SourceSize); +end; + +function GetFittedSlotRect(const SourceBounds, SlotRect: TPlayerSlotRect; MaxScale: real): TPlayerSlotRect; +var + Scale: real; +begin + Result := SlotRect; + + if (SourceBounds.W <= 0) or (SourceBounds.H <= 0) or (SlotRect.W <= 0) or (SlotRect.H <= 0) then + Exit; + + Scale := Min(MaxScale, Min(SlotRect.W / Max(1.0, SourceBounds.W), SlotRect.H / Max(1.0, SourceBounds.H))); + Result.W := Max(1, Round(SourceBounds.W * Scale)); + Result.H := Max(1, Round(SourceBounds.H * Scale)); + Result.X := SlotRect.X + (SlotRect.W - Result.W) div 2; + Result.Y := SlotRect.Y; +end; + +end. diff --git a/src/ultrastardx-unix.lpi b/src/ultrastardx-unix.lpi index 04b8826ae..e283ebbc0 100644 --- a/src/ultrastardx-unix.lpi +++ b/src/ultrastardx-unix.lpi @@ -544,6 +544,11 @@ + + + + + diff --git a/src/ultrastardx-win.lpi b/src/ultrastardx-win.lpi index 80b5be170..78e995725 100644 --- a/src/ultrastardx-win.lpi +++ b/src/ultrastardx-win.lpi @@ -560,6 +560,10 @@ + + + + diff --git a/src/ultrastardx.dpr b/src/ultrastardx.dpr index f319dedf8..9df63db22 100644 --- a/src/ultrastardx.dpr +++ b/src/ultrastardx.dpr @@ -215,6 +215,7 @@ uses UPathUtils in 'base\UPathUtils.pas', UNote in 'base\UNote.pas', UBeatTimer in 'base\UBeatTimer.pas', + UPlayerLayout in 'base\UPlayerLayout.pas', TextGL in 'base\TextGL.pas', UUnicodeUtils in 'base\UUnicodeUtils.pas', From 7937ee8a74360b193dc112d5d7fd712d939dae57 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:28:53 +0200 Subject: [PATCH 04/11] Add generic player layout theme support --- src/base/UIni.pas | 46 +++ src/base/UThemes.pas | 711 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 682 insertions(+), 75 deletions(-) diff --git a/src/base/UIni.pas b/src/base/UIni.pas index 27baafe11..0387fd862 100644 --- a/src/base/UIni.pas +++ b/src/base/UIni.pas @@ -93,6 +93,8 @@ TInputDeviceConfig = record IPlayers: array[0..4] of UTF8String = ('1', '2', '3', '4', '6'); IPlayersVals: array[0..4] of integer = ( 1 , 2 , 3 , 4 , 6 ); +function TryGetMixedPlayerColorPair(ColorIndex: integer; out LeftColor, RightColor: integer): boolean; + type //Options @@ -616,6 +618,50 @@ TSafeIniFile = class(TIniFile) const IGNORE_INDEX = -1; + BASE_PLAYER_COLOR_COUNT = 16; + +function TryGetMixedPlayerColorPair(ColorIndex: integer; out LeftColor, RightColor: integer): boolean; +var + Remaining: integer; + Sum: integer; + LeftCandidate: integer; + RightCandidate: integer; +begin + Result := false; + LeftColor := 0; + RightColor := 0; + + if ColorIndex <= BASE_PLAYER_COLOR_COUNT then + Exit; + + Remaining := ColorIndex - BASE_PLAYER_COLOR_COUNT; + for Sum := BASE_PLAYER_COLOR_COUNT + 1 to BASE_PLAYER_COLOR_COUNT * 2 - 1 do + begin + LeftCandidate := Sum - BASE_PLAYER_COLOR_COUNT; + if LeftCandidate < 1 then + LeftCandidate := 1; + while LeftCandidate <= ((Sum - 1) div 2) do + begin + RightCandidate := Sum - LeftCandidate; + if LeftCandidate >= RightCandidate then + begin + Inc(LeftCandidate); + Continue; + end; + + Dec(Remaining); + if Remaining = 0 then + begin + LeftColor := LeftCandidate; + RightColor := RightCandidate; + Result := true; + Exit; + end; + + Inc(LeftCandidate); + end; + end; +end; constructor TSafeIniFile.Create(const FileName, LogSource: string); begin diff --git a/src/base/UThemes.pas b/src/base/UThemes.pas index 31d3791bd..4f303d505 100644 --- a/src/base/UThemes.pas +++ b/src/base/UThemes.pas @@ -40,6 +40,7 @@ interface UCommon, ULog, UIni, + UPlayerLayout, UTexture, UPath; type @@ -288,6 +289,46 @@ TThemeSingPlayer = record Oscilloscope: TThemePosition; end; + TThemeNamePlayerSelectLayout = record + Area: TPlayerSlotRect; + MinPlayerCount: integer; + MaxColumns: integer; + RowHeightDivisor: real; + MaxScaleSmall: real; + MaxScaleMedium: real; + MaxScaleDefault: real; + end; + + TThemeSingPlayerWidgetLayout = record + MinFrameW: integer; + MinFrameH: integer; + MinScoreW: integer; + MinScoreH: integer; + HeaderOffsetLeft: integer; + HeaderOffsetTopBase: integer; + HeaderOffsetTopPerExtraRow: integer; + MinAvatarInsetX: integer; + MinAvatarInsetY: integer; + NameGapBaseX: integer; + NameGapMinX: integer; + NamePaddingBaseX: integer; + NamePaddingMinX: integer; + NamePaddingBaseY: integer; + NamePaddingMinY: integer; + NameMinW: integer; + NameMinH: integer; + NameMinSize: integer; + ScoreWidthFraction: real; + OscilloscopeGapBaseY: integer; + OscilloscopeGapMinY: integer; + OscilloscopeMinW: integer; + OscilloscopeMinH: integer; + PopupYOffsetSolo: integer; + PopupYOffsetDuet: integer; + PopupFontSizeSolo: integer; + PopupFontSizeDuet: integer; + end; + TThemeLoading = class(TThemeBasic) StaticAnimation: TThemeStatic; TextLoading: TThemeText; @@ -320,11 +361,15 @@ TThemeName = class(TThemeBasic) PlayerAvatar: TThemeButton; + PlayerSelectTemplateFrame: TThemeStatic; + PlayerSelectTemplateText: TThemeText; + PlayerSelectTemplateAvatar: TThemeStaticRectangle; + PlayerSelectLayout: TThemeNamePlayerSelectLayout; PlayerSelect: array [0..UIni.IMaxPlayerCount-1] of TThemeStatic; PlayerSelectText: array [0..UIni.IMaxPlayerCount-1] of TThemeText; PlayerSelectAvatar: array [0..UIni.IMaxPlayerCount-1] of TThemeStaticRectangle; PlayerSelectCurrent: TThemeButton; - + SelectPlayersCount: TThemeSelectSlide; SelectPlayerColor: TThemeSelectSlide; SelectPlayerLevel: TThemeSelectSlide; @@ -466,6 +511,7 @@ TThemeSong = class(TThemeBasic) Text3PlayersDuetSingerP1: TThemeText; Text3PlayersDuetSingerP2: TThemeText; Text3PlayersDuetSingerP3: TThemeText; + DuetSingerArea: TThemePosition; end; TThemeSing = class(TThemeBasic) @@ -518,6 +564,10 @@ TThemeSing = class(TThemeBasic) Duet6PP5: TThemeSingPlayer; Duet6PP6: TThemeSingPlayer; + PlayerTemplate: TThemeSingPlayer; + PlayerLayout: TSingPlayerLayoutConfig; + PlayerWidgetLayout: TThemeSingPlayerWidgetLayout; + StaticSongName: TThemeStatic; TextSongName: TThemeText; @@ -614,6 +664,8 @@ TThemeScore = class(TThemeBasic) TextTitle: TThemeText; TextArtistTitle: TThemeText; + PlayerLayoutArea: TPlayerSlotRect; + PlayerLayoutExtraColsBias: integer; PlayerStatic: array[1..UIni.IMaxPlayerCount] of AThemeStatic; PlayerTexts: array[1..UIni.IMaxPlayerCount] of AThemeText; @@ -643,7 +695,7 @@ TThemeScore = class(TThemeBasic) StaticLevel: array[1..UIni.IMaxPlayerCount] of TThemeStatic; StaticLevelRound: array[1..UIni.IMaxPlayerCount] of TThemeStatic; - ButtonSend: array[1..UIni.IMaxPlayerCount] of TThemeButton; + ButtonSend: array[1..3] of TThemeButton; StaticNavigate: TThemeStatic; TextNavigate: TThemeText; @@ -1223,6 +1275,7 @@ TTheme = class procedure ThemeLoadPosition(var ThemePosition: TThemePosition; const Name: string); procedure ThemeLoadLyricBar(var LyricBar: TThemeLyricBar; const Name: string); + procedure ThemeScoreLoadData; procedure ThemeScoreLoad; procedure ThemePartyLoad; procedure ThemeSongLoad; @@ -1251,6 +1304,18 @@ function GetLyricGrayColor(Color: integer): TRGB; function GetLyricOutlineColor(Color: integer): TRGB; function GetLyricBarColor(Color: integer): TRGB; +function GetThemePlayerBounds(const ThemePlayer: TThemeSingPlayer): TPlayerSlotRect; +function GetThemePlayerScoreLayoutBounds(const BaseBounds, LayoutArea: TPlayerSlotRect; + PlayerCountOnScreen: integer): TPlayerSlotRect; +function GetNamePlayerSelectBaseBounds(const NameTheme: TThemeName): TPlayerSlotRect; +function GetNamePlayerSelectLayoutBounds(const BaseBounds: TPlayerSlotRect; + const Layout: TThemeNamePlayerSelectLayout): TPlayerSlotRect; +function GetNamePlayerSelectSlotRect(PlayerIndex, PlayerCount: integer; + const BaseBounds, LayoutBounds: TPlayerSlotRect; const Layout: TThemeNamePlayerSelectLayout): TPlayerSlotRect; +function GetNamePlayerSelectMaxScale(PlayerCount: integer; const Layout: TThemeNamePlayerSelectLayout): real; +function GetSingHeaderTopOffset(const Layout: TThemeSingPlayerWidgetLayout; + RowCount: integer; Scale: real): integer; + function GetPlayerColor(Color: integer): TRGB; function GetPlayerLightColor(Color: integer): TRGB; procedure LoadPlayersColors; @@ -1277,6 +1342,350 @@ implementation const MAX_INHERITANCE = 10; +type + TScoreSlotThemeTemplate = record + PlayerStatic: AThemeStatic; + PlayerTexts: AThemeText; + TextName: TThemeText; + TextScore: TThemeText; + AvatarStatic: TThemeStatic; + TextNotes: TThemeText; + TextNotesScore: TThemeText; + TextLineBonus: TThemeText; + TextLineBonusScore: TThemeText; + TextGoldenNotes: TThemeText; + TextGoldenNotesScore: TThemeText; + TextTotal: TThemeText; + TextTotalScore: TThemeText; + StaticBoxLightest: TThemeStatic; + StaticBoxLight: TThemeStatic; + StaticBoxDark: TThemeStatic; + StaticRatings: TThemeStatic; + StaticBackLevel: TThemeStatic; + StaticBackLevelRound: TThemeStatic; + StaticLevel: TThemeStatic; + StaticLevelRound: TThemeStatic; + end; + + TNamePlayerSelectTemplate = record + Frame: TThemeStatic; + Text: TThemeText; + Avatar: TThemeStaticRectangle; + end; + +procedure ExpandScoreBounds(var Bounds: TPlayerSlotRect; X, Y, W, H: integer; var Initialized: boolean); +var + MaxX: integer; + MaxY: integer; +begin + MaxX := X + W; + MaxY := Y + H; + + if not Initialized then + begin + Bounds.X := X; + Bounds.Y := Y; + Bounds.W := W; + Bounds.H := H; + Initialized := true; + Exit; + end; + + Bounds.W := Max(Bounds.X + Bounds.W, MaxX) - Min(Bounds.X, X); + Bounds.H := Max(Bounds.Y + Bounds.H, MaxY) - Min(Bounds.Y, Y); + Bounds.X := Min(Bounds.X, X); + Bounds.Y := Min(Bounds.Y, Y); +end; + +function GetScoreTemplateBounds(const Template: TScoreSlotThemeTemplate): TPlayerSlotRect; +var + Initialized: boolean; + I: integer; +begin + Initialized := false; + + ExpandScoreBounds(Result, Template.TextName.X, Template.TextName.Y, Template.TextName.W, Template.TextName.H, Initialized); + ExpandScoreBounds(Result, Template.TextScore.X, Template.TextScore.Y, Template.TextScore.W, Template.TextScore.H, Initialized); + ExpandScoreBounds(Result, Template.AvatarStatic.X, Template.AvatarStatic.Y, Template.AvatarStatic.W, Template.AvatarStatic.H, Initialized); + ExpandScoreBounds(Result, Template.TextNotes.X, Template.TextNotes.Y, Template.TextNotes.W, Template.TextNotes.H, Initialized); + ExpandScoreBounds(Result, Template.TextNotesScore.X, Template.TextNotesScore.Y, Template.TextNotesScore.W, Template.TextNotesScore.H, Initialized); + ExpandScoreBounds(Result, Template.TextLineBonus.X, Template.TextLineBonus.Y, Template.TextLineBonus.W, Template.TextLineBonus.H, Initialized); + ExpandScoreBounds(Result, Template.TextLineBonusScore.X, Template.TextLineBonusScore.Y, Template.TextLineBonusScore.W, Template.TextLineBonusScore.H, Initialized); + ExpandScoreBounds(Result, Template.TextGoldenNotes.X, Template.TextGoldenNotes.Y, Template.TextGoldenNotes.W, Template.TextGoldenNotes.H, Initialized); + ExpandScoreBounds(Result, Template.TextGoldenNotesScore.X, Template.TextGoldenNotesScore.Y, Template.TextGoldenNotesScore.W, Template.TextGoldenNotesScore.H, Initialized); + ExpandScoreBounds(Result, Template.TextTotal.X, Template.TextTotal.Y, Template.TextTotal.W, Template.TextTotal.H, Initialized); + ExpandScoreBounds(Result, Template.TextTotalScore.X, Template.TextTotalScore.Y, Template.TextTotalScore.W, Template.TextTotalScore.H, Initialized); + ExpandScoreBounds(Result, Template.StaticBoxLightest.X, Template.StaticBoxLightest.Y, Template.StaticBoxLightest.W, Template.StaticBoxLightest.H, Initialized); + ExpandScoreBounds(Result, Template.StaticBoxLight.X, Template.StaticBoxLight.Y, Template.StaticBoxLight.W, Template.StaticBoxLight.H, Initialized); + ExpandScoreBounds(Result, Template.StaticBoxDark.X, Template.StaticBoxDark.Y, Template.StaticBoxDark.W, Template.StaticBoxDark.H, Initialized); + ExpandScoreBounds(Result, Template.StaticRatings.X, Template.StaticRatings.Y, Template.StaticRatings.W, Template.StaticRatings.H, Initialized); + ExpandScoreBounds(Result, Template.StaticBackLevel.X, Template.StaticBackLevel.Y, Template.StaticBackLevel.W, Template.StaticBackLevel.H, Initialized); + ExpandScoreBounds(Result, Template.StaticBackLevelRound.X, Template.StaticBackLevelRound.Y, Template.StaticBackLevelRound.W, Template.StaticBackLevelRound.H, Initialized); + ExpandScoreBounds(Result, Template.StaticLevel.X, Template.StaticLevel.Y, Template.StaticLevel.W, Template.StaticLevel.H, Initialized); + ExpandScoreBounds(Result, Template.StaticLevelRound.X, Template.StaticLevelRound.Y, Template.StaticLevelRound.W, Template.StaticLevelRound.H, Initialized); + + for I := 0 to High(Template.PlayerStatic) do + ExpandScoreBounds(Result, Template.PlayerStatic[I].X, Template.PlayerStatic[I].Y, Template.PlayerStatic[I].W, Template.PlayerStatic[I].H, Initialized); + + for I := 0 to High(Template.PlayerTexts) do + ExpandScoreBounds(Result, Template.PlayerTexts[I].X, Template.PlayerTexts[I].Y, Template.PlayerTexts[I].W, Template.PlayerTexts[I].H, Initialized); +end; + +function GetThemePlayerBounds(const ThemePlayer: TThemeSingPlayer): TPlayerSlotRect; +var + MinX: integer; + MinY: integer; + MaxX: integer; + MaxY: integer; +begin + MinX := Min(ThemePlayer.ScoreBackground.X, ThemePlayer.SingBar.X); + MinY := Min(ThemePlayer.ScoreBackground.Y, ThemePlayer.Score.Y); + MaxX := Max(ThemePlayer.ScoreBackground.X + ThemePlayer.ScoreBackground.W, + ThemePlayer.SingBar.X + ThemePlayer.SingBar.W); + MaxY := Max(ThemePlayer.ScoreBackground.Y + ThemePlayer.ScoreBackground.H, + ThemePlayer.SingBar.Y + ThemePlayer.SingBar.H); + + Result.X := MinX; + Result.Y := MinY; + Result.W := MaxX - MinX; + Result.H := MaxY - MinY; +end; + +function GetNamePlayerSelectTemplateBounds(const Template: TNamePlayerSelectTemplate): TPlayerSlotRect; +var + Initialized: boolean; +begin + Initialized := false; + ExpandScoreBounds(Result, Template.Frame.X, Template.Frame.Y, Template.Frame.W, Template.Frame.H, Initialized); + ExpandScoreBounds(Result, Template.Avatar.X, Template.Avatar.Y, Template.Avatar.W, Template.Avatar.H, Initialized); + ExpandScoreBounds(Result, Template.Text.X, Template.Text.Y, Max(Template.Frame.W, 1), Max(Template.Text.H, 1), Initialized); +end; + +function GetNamePlayerSelectBaseBounds(const NameTheme: TThemeName): TPlayerSlotRect; +var + Initialized: boolean; +begin + Initialized := false; + ExpandScoreBounds(Result, NameTheme.PlayerSelectTemplateFrame.X, NameTheme.PlayerSelectTemplateFrame.Y, + NameTheme.PlayerSelectTemplateFrame.W, NameTheme.PlayerSelectTemplateFrame.H, Initialized); + ExpandScoreBounds(Result, NameTheme.PlayerSelectTemplateAvatar.X, NameTheme.PlayerSelectTemplateAvatar.Y, + NameTheme.PlayerSelectTemplateAvatar.W, NameTheme.PlayerSelectTemplateAvatar.H, Initialized); + ExpandScoreBounds(Result, NameTheme.PlayerSelectTemplateText.X, NameTheme.PlayerSelectTemplateText.Y, + Max(NameTheme.PlayerSelectTemplateFrame.W, 1), Max(NameTheme.PlayerSelectTemplateText.H, 1), Initialized); +end; + +function GetNamePlayerSelectLayoutBounds(const BaseBounds: TPlayerSlotRect; + const Layout: TThemeNamePlayerSelectLayout): TPlayerSlotRect; +begin + Result := Layout.Area; + + if BaseBounds.W > 0 then + Result.W := Max(Result.W, BaseBounds.W); + if BaseBounds.H > 0 then + Result.H := Max(Result.H, BaseBounds.H); +end; + +function GetNamePlayerSelectSlotRect(PlayerIndex, PlayerCount: integer; + const BaseBounds, LayoutBounds: TPlayerSlotRect; const Layout: TThemeNamePlayerSelectLayout): TPlayerSlotRect; +var + LayoutPlayerCount: integer; + Cols: integer; + Rows: integer; + ColIndex: integer; + RowIndex: integer; + SlotWidth: integer; + SlotHeight: integer; +begin + Result := BaseBounds; + + if (PlayerIndex < 0) or (PlayerIndex >= PlayerCount) then + Exit; + + LayoutPlayerCount := Max(PlayerCount, Layout.MinPlayerCount); + Cols := Min(Layout.MaxColumns, LayoutPlayerCount); + Rows := (LayoutPlayerCount + Cols - 1) div Cols; + if (Cols <= 0) or (Rows <= 0) then + Exit; + + ColIndex := PlayerIndex mod Cols; + RowIndex := PlayerIndex div Cols; + + SlotWidth := LayoutBounds.W div Cols; + SlotHeight := LayoutBounds.H div Rows; + + Result.X := LayoutBounds.X + ColIndex * SlotWidth; + Result.Y := LayoutBounds.Y + Round(RowIndex * (SlotHeight / Max(Layout.RowHeightDivisor, 1.0))); + + if ColIndex = Cols - 1 then + Result.W := LayoutBounds.X + LayoutBounds.W - Result.X + else + Result.W := SlotWidth; + + if RowIndex = Rows - 1 then + Result.H := LayoutBounds.Y + LayoutBounds.H - Result.Y + else + Result.H := SlotHeight; +end; + +function GetNamePlayerSelectMaxScale(PlayerCount: integer; const Layout: TThemeNamePlayerSelectLayout): real; +begin + if PlayerCount <= 2 then + Exit(Layout.MaxScaleSmall); + if PlayerCount <= 4 then + Exit(Layout.MaxScaleMedium); + Result := Layout.MaxScaleDefault; +end; + +function GetSingHeaderTopOffset(const Layout: TThemeSingPlayerWidgetLayout; + RowCount: integer; Scale: real): integer; +var + BaseOffset: integer; +begin + BaseOffset := Layout.HeaderOffsetTopBase + + Max(0, RowCount - 1) * Layout.HeaderOffsetTopPerExtraRow; + Result := Round(BaseOffset * Scale); +end; + +function TransformNamePlayerSelectStatic(const Source: TThemeStatic; const SourceBounds, SlotRect: TPlayerSlotRect): TThemeStatic; +var + FittedRect: TPlayerSlotRect; +begin + Result := Source; + FittedRect := GetFittedSlotRect(SourceBounds, SlotRect, 1.0); + Result.X := ScaleCoordToSlot(Source.X, SourceBounds.X, SourceBounds.W, FittedRect.X, FittedRect.W); + Result.Y := ScaleCoordToSlot(Source.Y, SourceBounds.Y, SourceBounds.H, FittedRect.Y, FittedRect.H); + Result.W := ScaleLengthToSlot(Source.W, SourceBounds.W, FittedRect.W); + Result.H := ScaleLengthToSlot(Source.H, SourceBounds.H, FittedRect.H); +end; + +function TransformNamePlayerSelectText(const Source: TThemeText; const SourceBounds, SlotRect: TPlayerSlotRect): TThemeText; +var + FittedRect: TPlayerSlotRect; +begin + Result := Source; + FittedRect := GetFittedSlotRect(SourceBounds, SlotRect, 1.0); + Result.X := ScaleCoordToSlot(Source.X, SourceBounds.X, SourceBounds.W, FittedRect.X, FittedRect.W); + Result.Y := ScaleCoordToSlot(Source.Y, SourceBounds.Y, SourceBounds.H, FittedRect.Y, FittedRect.H); + Result.Size := Max(8, ScaleLengthToSlot(Source.Size, SourceBounds.H, FittedRect.H)); +end; + +function TransformNamePlayerSelectAvatar(const Source: TThemeStaticRectangle; const SourceBounds, SlotRect: TPlayerSlotRect): TThemeStaticRectangle; +var + FittedRect: TPlayerSlotRect; +begin + Result := Source; + FittedRect := GetFittedSlotRect(SourceBounds, SlotRect, 1.0); + Result.X := ScaleCoordToSlot(Source.X, SourceBounds.X, SourceBounds.W, FittedRect.X, FittedRect.W); + Result.Y := ScaleCoordToSlot(Source.Y, SourceBounds.Y, SourceBounds.H, FittedRect.Y, FittedRect.H); + Result.W := ScaleLengthToSlot(Source.W, SourceBounds.W, FittedRect.W); + Result.H := ScaleLengthToSlot(Source.H, SourceBounds.H, FittedRect.H); +end; + +procedure AssignNamePlayerSelectSlot(var NameTheme: TThemeName; const Template: TNamePlayerSelectTemplate; + const SourceBounds, LayoutBounds: TPlayerSlotRect; PlayerIndex: integer); +var + SlotRect: TPlayerSlotRect; +begin + SlotRect := GetNamePlayerSelectSlotRect(PlayerIndex, UIni.IMaxPlayerCount, SourceBounds, LayoutBounds, NameTheme.PlayerSelectLayout); + NameTheme.PlayerSelect[PlayerIndex] := TransformNamePlayerSelectStatic(Template.Frame, SourceBounds, SlotRect); + NameTheme.PlayerSelectText[PlayerIndex] := TransformNamePlayerSelectText(Template.Text, SourceBounds, SlotRect); + NameTheme.PlayerSelectAvatar[PlayerIndex] := TransformNamePlayerSelectAvatar(Template.Avatar, SourceBounds, SlotRect); +end; + +function GetThemePlayerScoreLayoutBounds(const BaseBounds, LayoutArea: TPlayerSlotRect; + PlayerCountOnScreen: integer): TPlayerSlotRect; +var + Grid: TPlayerGrid; +begin + Grid := GetScorePlayerGrid(PlayerCountOnScreen, LayoutArea.W, LayoutArea.H, + Theme.Score.PlayerLayoutExtraColsBias); + Result := GetScaledGridLayoutBounds(BaseBounds, LayoutArea, Grid); +end; + +function TransformScoreStatic(const Source: TThemeStatic; const SourceBounds, SlotRect: TPlayerSlotRect): TThemeStatic; +var + Scale: real; +begin + Result := Source; + Result.X := ScaleCoordToSlot(Source.X, SourceBounds.X, SourceBounds.W, SlotRect.X, SlotRect.W); + Result.Y := ScaleCoordToSlot(Source.Y, SourceBounds.Y, SourceBounds.H, SlotRect.Y, SlotRect.H); + Result.W := ScaleLengthToSlot(Source.W, SourceBounds.W, SlotRect.W); + Result.H := ScaleLengthToSlot(Source.H, SourceBounds.H, SlotRect.H); + // Preserve 1px theme separators as hairlines instead of scaling them into bands. + if Source.W = 1 then + Result.W := 1; + if Source.H = 1 then + Result.H := 1; + Scale := Min(SlotRect.W / Max(1.0, SourceBounds.W), SlotRect.H / Max(1.0, SourceBounds.H)); + Result.Reflectionspacing := Source.Reflectionspacing * Scale; +end; + +function TransformScoreText(const Source: TThemeText; const SourceBounds, SlotRect: TPlayerSlotRect): TThemeText; +var + Scale: real; +begin + Result := Source; + Result.X := ScaleCoordToSlot(Source.X, SourceBounds.X, SourceBounds.W, SlotRect.X, SlotRect.W); + Result.Y := ScaleCoordToSlot(Source.Y, SourceBounds.Y, SourceBounds.H, SlotRect.Y, SlotRect.H); + Result.W := ScaleLengthToSlot(Source.W, SourceBounds.W, SlotRect.W); + Result.H := ScaleLengthToSlot(Source.H, SourceBounds.H, SlotRect.H); + + Scale := Min(SlotRect.W / Max(1.0, SourceBounds.W), SlotRect.H / Max(1.0, SourceBounds.H)); + Result.Size := Max(1, Round(Source.Size * Scale)); + Result.ReflectionSpacing := Source.ReflectionSpacing * Scale; +end; + +function TransformScoreStatics(const Source: AThemeStatic; const SourceBounds, SlotRect: TPlayerSlotRect): AThemeStatic; +var + I: integer; +begin + SetLength(Result, Length(Source)); + for I := 0 to High(Source) do + Result[I] := TransformScoreStatic(Source[I], SourceBounds, SlotRect); +end; + +function TransformScoreTexts(const Source: AThemeText; const SourceBounds, SlotRect: TPlayerSlotRect): AThemeText; +var + I: integer; +begin + SetLength(Result, Length(Source)); + for I := 0 to High(Source) do + Result[I] := TransformScoreText(Source[I], SourceBounds, SlotRect); +end; + +procedure AssignScoreThemeSlot(var ScoreTheme: TThemeScore; const Template: TScoreSlotThemeTemplate; + const SourceBounds, LayoutBounds: TPlayerSlotRect; LayoutPlayerCount, LayoutPlayerIndex, TargetSlotIndex: integer); +var + SlotRect: TPlayerSlotRect; + Grid: TPlayerGrid; +begin + Grid := GetScorePlayerGrid(LayoutPlayerCount, LayoutBounds.W, LayoutBounds.H, + ScoreTheme.PlayerLayoutExtraColsBias); + SlotRect := GetPlayerSlotRect(LayoutPlayerIndex, Grid, LayoutBounds.X, LayoutBounds.Y, LayoutBounds.W, LayoutBounds.H); + + ScoreTheme.PlayerStatic[TargetSlotIndex] := TransformScoreStatics(Template.PlayerStatic, SourceBounds, SlotRect); + ScoreTheme.PlayerTexts[TargetSlotIndex] := TransformScoreTexts(Template.PlayerTexts, SourceBounds, SlotRect); + ScoreTheme.TextName[TargetSlotIndex] := TransformScoreText(Template.TextName, SourceBounds, SlotRect); + ScoreTheme.TextScore[TargetSlotIndex] := TransformScoreText(Template.TextScore, SourceBounds, SlotRect); + ScoreTheme.AvatarStatic[TargetSlotIndex] := TransformScoreStatic(Template.AvatarStatic, SourceBounds, SlotRect); + ScoreTheme.TextNotes[TargetSlotIndex] := TransformScoreText(Template.TextNotes, SourceBounds, SlotRect); + ScoreTheme.TextNotesScore[TargetSlotIndex] := TransformScoreText(Template.TextNotesScore, SourceBounds, SlotRect); + ScoreTheme.TextLineBonus[TargetSlotIndex] := TransformScoreText(Template.TextLineBonus, SourceBounds, SlotRect); + ScoreTheme.TextLineBonusScore[TargetSlotIndex] := TransformScoreText(Template.TextLineBonusScore, SourceBounds, SlotRect); + ScoreTheme.TextGoldenNotes[TargetSlotIndex] := TransformScoreText(Template.TextGoldenNotes, SourceBounds, SlotRect); + ScoreTheme.TextGoldenNotesScore[TargetSlotIndex] := TransformScoreText(Template.TextGoldenNotesScore, SourceBounds, SlotRect); + ScoreTheme.TextTotal[TargetSlotIndex] := TransformScoreText(Template.TextTotal, SourceBounds, SlotRect); + ScoreTheme.TextTotalScore[TargetSlotIndex] := TransformScoreText(Template.TextTotalScore, SourceBounds, SlotRect); + ScoreTheme.StaticBoxLightest[TargetSlotIndex] := TransformScoreStatic(Template.StaticBoxLightest, SourceBounds, SlotRect); + ScoreTheme.StaticBoxLight[TargetSlotIndex] := TransformScoreStatic(Template.StaticBoxLight, SourceBounds, SlotRect); + ScoreTheme.StaticBoxDark[TargetSlotIndex] := TransformScoreStatic(Template.StaticBoxDark, SourceBounds, SlotRect); + ScoreTheme.StaticRatings[TargetSlotIndex] := TransformScoreStatic(Template.StaticRatings, SourceBounds, SlotRect); + ScoreTheme.StaticBackLevel[TargetSlotIndex] := TransformScoreStatic(Template.StaticBackLevel, SourceBounds, SlotRect); + ScoreTheme.StaticBackLevelRound[TargetSlotIndex] := TransformScoreStatic(Template.StaticBackLevelRound, SourceBounds, SlotRect); + ScoreTheme.StaticLevel[TargetSlotIndex] := TransformScoreStatic(Template.StaticLevel, SourceBounds, SlotRect); + ScoreTheme.StaticLevelRound[TargetSlotIndex] := TransformScoreStatic(Template.StaticLevelRound, SourceBounds, SlotRect); +end; + //----------- //Helper procs to use TRGB in Opengl ...maybe this should be somewhere else //----------- @@ -1461,6 +1870,10 @@ function TTheme.LoadTheme(ThemeNum: integer; sColor: integer): boolean; var I, J: integer; IniFile: TMemIniFile; + SectionList: TThemeSectionList; + NamePlayerSelectTemplate: TNamePlayerSelectTemplate; + NamePlayerSelectBounds: TPlayerSlotRect; + NamePlayerSelectLayoutBounds: TPlayerSlotRect; begin Result := false; @@ -1551,12 +1964,36 @@ function TTheme.LoadTheme(ThemeNum: integer; sColor: integer): boolean; ThemeLoadSelectSlide(Name.SelectPlayerColor, 'NameSelectPlayerColor'); ThemeLoadSelectSlide(Name.SelectPlayerLevel, 'NameSelectPlayerLevel'); + ThemeLoadStatic(NamePlayerSelectTemplate.Frame, 'NamePlayerSelectTemplateFrame'); + ThemeLoadText(NamePlayerSelectTemplate.Text, 'NamePlayerSelectTemplateText'); + ThemeLoadStaticRectangle(NamePlayerSelectTemplate.Avatar, 'NamePlayerSelectTemplateAvatar'); + Name.PlayerSelectTemplateFrame := NamePlayerSelectTemplate.Frame; + Name.PlayerSelectTemplateText := NamePlayerSelectTemplate.Text; + Name.PlayerSelectTemplateAvatar := NamePlayerSelectTemplate.Avatar; + SectionList := GetSectionList('NamePlayerSelectGrid'); + if Length(SectionList) = 0 then + SectionList := GetSectionList('NamePlayerSelectLayout'); + Name.PlayerSelectLayout.Area.X := ReadInteger(SectionList, 'GridX', ReadInteger(SectionList, 'X', 30)); + Name.PlayerSelectLayout.Area.Y := ReadInteger(SectionList, 'GridY', ReadInteger(SectionList, 'Y', 185)); + Name.PlayerSelectLayout.Area.W := ReadInteger(SectionList, 'GridW', ReadInteger(SectionList, 'W', 760)); + Name.PlayerSelectLayout.Area.H := ReadInteger(SectionList, 'GridH', ReadInteger(SectionList, 'H', 155)); + Name.PlayerSelectLayout.MinPlayerCount := ReadInteger(SectionList, 'MinimumVisibleSlots', + ReadInteger(SectionList, 'MinPlayerCount', 2)); + Name.PlayerSelectLayout.MaxColumns := ReadInteger(SectionList, 'MaximumColumns', + ReadInteger(SectionList, 'MaxColumns', 12)); + Name.PlayerSelectLayout.RowHeightDivisor := ReadFloat(SectionList, 'RowVerticalSpreadDivisor', + ReadFloat(SectionList, 'RowHeightDivisor', 1.5)); + Name.PlayerSelectLayout.MaxScaleSmall := ReadFloat(SectionList, 'MaxScaleUpTo2Players', + ReadFloat(SectionList, 'MaxScaleSmall', 1.35)); + Name.PlayerSelectLayout.MaxScaleMedium := ReadFloat(SectionList, 'MaxScaleUpTo4Players', + ReadFloat(SectionList, 'MaxScaleMedium', 1.15)); + Name.PlayerSelectLayout.MaxScaleDefault := ReadFloat(SectionList, 'MaxScaleDefault', + ReadFloat(SectionList, 'MaxScaleDefault', 1.0)); + NamePlayerSelectBounds := GetNamePlayerSelectTemplateBounds(NamePlayerSelectTemplate); + NamePlayerSelectLayoutBounds := GetNamePlayerSelectLayoutBounds(NamePlayerSelectBounds, Name.PlayerSelectLayout); + for I := 0 to UIni.IMaxPlayerCount-1 do - begin - ThemeLoadOptionalStatic(Name.PlayerSelect[I], 'NamePlayerSelectStatic' + IntToStr((I + 1))); - ThemeLoadText(Name.PlayerSelectText[I], 'NamePlayerSelectStatic' + IntToStr((I + 1)) + 'Text'); - ThemeLoadOptionalStaticRectangle(Name.PlayerSelectAvatar[I], 'NamePlayerSelectStatic' + IntToStr((I + 1)) + 'Avatar'); - end; + AssignNamePlayerSelectSlot(Name, NamePlayerSelectTemplate, NamePlayerSelectBounds, NamePlayerSelectLayoutBounds, I); ThemeLoadButton(Name.PlayerSelectCurrent, 'NamePlayerSelectCurrent'); @@ -1702,9 +2139,6 @@ function TTheme.LoadTheme(ThemeNum: integer; sColor: integer): boolean; ThemeLoadPosition(Sing.Solo3PP3.SingBar, 'SingP3SingBar'); ThemeLoadPosition(Sing.Solo3PP3.Oscilloscope, 'SingP3ROscilloscope'); - ThemeLoadStatic(Sing.StaticSongName, 'SingSongNameStatic'); - ThemeLoadText(Sing.TextSongName, 'SingSongNameText'); - // 3/6 players duet ThemeLoadSingPlayerStatics(Sing.Duet3PP1, 'DuetP1ThreeP'); ThemeLoadSingPlayerStatics(Sing.Duet3PP2, 'DuetP2M'); @@ -1736,6 +2170,72 @@ function TTheme.LoadTheme(ThemeNum: integer; sColor: integer): boolean; ThemeLoadSingPlayerStatics(Sing.Duet6PP5, 'P5DuetSixP'); ThemeLoadSingPlayerStatics(Sing.Duet6PP6, 'P6DuetSixP'); + ThemeLoadSingPlayerStatics(Sing.PlayerTemplate, 'PlayerTemplate'); + SectionList := GetSectionList('SingPlayerGrid'); + Sing.PlayerLayout.ColumnContainerLeft := ReadInteger(SectionList, 'LaneAreaX', 40); + Sing.PlayerLayout.ColumnContainerTopReserved := ReadInteger(SectionList, 'LaneAreaY', 120); + Sing.PlayerLayout.ColumnContainerTopNoLyrics := ReadInteger(SectionList, 'LaneAreaYNoLyrics', + Sing.PlayerLayout.ColumnContainerTopReserved); + Sing.PlayerLayout.ColumnContainerWidth := ReadInteger(SectionList, 'LaneAreaW', 750); + Sing.PlayerLayout.LaneHeightReserved := ReadInteger(SectionList, 'LaneAreaHReserved', 335); + Sing.PlayerLayout.LaneHeightNoLyrics := ReadInteger(SectionList, 'LaneAreaHNoLyrics', 395); + Sing.PlayerLayout.ColumnGap := ReadInteger(SectionList, 'LaneColumnGap', 50); + Sing.PlayerLayout.RowGap := ReadInteger(SectionList, 'LaneRowGap', 0); + Sing.PlayerLayout.GridExtraLeft := ReadInteger(SectionList, 'LaneGridLeftInset', 20); + Sing.PlayerLayout.BaseLineSpacing := ReadInteger(SectionList, 'LaneBaseLineSpacing', 15); + Sing.PlayerLayout.GuideLineCount := ReadInteger(SectionList, 'LaneGuideLineCount', 9); + Sing.PlayerLayout.RowAnchorGuideIndex := ReadInteger(SectionList, 'LaneRowAnchorGuideIndex', 7); + Sing.PlayerLayout.WidgetScaleBaseWidth := ReadInteger(SectionList, 'WidgetScaleReferenceW', 300); + Sing.PlayerLayout.WidgetScalePerPlayer := ReadFloat(SectionList, 'WidgetScalePerPlayer', 0.02); + Sing.PlayerLayout.WidgetScaleMin := ReadFloat(SectionList, 'WidgetScaleMin', 0.82); + + Sing.PlayerLayout.ColumnContainerWidth := Max(1, Sing.PlayerLayout.ColumnContainerWidth); + Sing.PlayerLayout.LaneHeightReserved := Max(1, Sing.PlayerLayout.LaneHeightReserved); + Sing.PlayerLayout.LaneHeightNoLyrics := Max(1, Sing.PlayerLayout.LaneHeightNoLyrics); + Sing.PlayerLayout.ColumnGap := Max(0, Sing.PlayerLayout.ColumnGap); + Sing.PlayerLayout.RowGap := Max(0, Sing.PlayerLayout.RowGap); + Sing.PlayerLayout.GridExtraLeft := Max(0, Sing.PlayerLayout.GridExtraLeft); + Sing.PlayerLayout.BaseLineSpacing := Max(1, Sing.PlayerLayout.BaseLineSpacing); + Sing.PlayerLayout.GuideLineCount := Max(2, Sing.PlayerLayout.GuideLineCount); + Sing.PlayerLayout.RowAnchorGuideIndex := EnsureRange(Sing.PlayerLayout.RowAnchorGuideIndex, + 0, Sing.PlayerLayout.GuideLineCount - 1); + Sing.PlayerLayout.WidgetScaleBaseWidth := Max(1, Sing.PlayerLayout.WidgetScaleBaseWidth); + Sing.PlayerLayout.WidgetScalePerPlayer := Max(0.0, Sing.PlayerLayout.WidgetScalePerPlayer); + Sing.PlayerLayout.WidgetScaleMin := EnsureRange(Sing.PlayerLayout.WidgetScaleMin, 0.1, 1.0); + + SectionList := GetSectionList('SingPlayerWidgetPlacement'); + Sing.PlayerWidgetLayout.MinFrameW := ReadInteger(SectionList, 'MinFrameW', 26); + Sing.PlayerWidgetLayout.MinFrameH := ReadInteger(SectionList, 'MinFrameH', 26); + Sing.PlayerWidgetLayout.MinScoreW := ReadInteger(SectionList, 'MinScoreW', 56); + Sing.PlayerWidgetLayout.MinScoreH := ReadInteger(SectionList, 'MinScoreH', 18); + Sing.PlayerWidgetLayout.HeaderOffsetLeft := ReadInteger(SectionList, 'HeaderLeftOffset', 30); + Sing.PlayerWidgetLayout.HeaderOffsetTopBase := ReadInteger(SectionList, 'HeaderTopOffsetBase', 40); + Sing.PlayerWidgetLayout.HeaderOffsetTopPerExtraRow := ReadInteger(SectionList, + 'HeaderTopOffsetPerExtraRow', 0); + Sing.PlayerWidgetLayout.MinAvatarInsetX := ReadInteger(SectionList, 'AvatarInsetMinX', 1); + Sing.PlayerWidgetLayout.MinAvatarInsetY := ReadInteger(SectionList, 'AvatarInsetMinY', 1); + Sing.PlayerWidgetLayout.NameGapBaseX := ReadInteger(SectionList, 'NameGapX', 10); + Sing.PlayerWidgetLayout.NameGapMinX := ReadInteger(SectionList, 'NameGapMinX', 8); + Sing.PlayerWidgetLayout.NamePaddingBaseX := ReadInteger(SectionList, 'NamePaddingX', 6); + Sing.PlayerWidgetLayout.NamePaddingMinX := ReadInteger(SectionList, 'NamePaddingMinX', 4); + Sing.PlayerWidgetLayout.NamePaddingBaseY := ReadInteger(SectionList, 'NamePaddingY', 6); + Sing.PlayerWidgetLayout.NamePaddingMinY := ReadInteger(SectionList, 'NamePaddingMinY', 3); + Sing.PlayerWidgetLayout.NameMinW := ReadInteger(SectionList, 'NameMinW', 24); + Sing.PlayerWidgetLayout.NameMinH := ReadInteger(SectionList, 'NameMinH', 14); + Sing.PlayerWidgetLayout.NameMinSize := ReadInteger(SectionList, 'NameMinSize', 10); + Sing.PlayerWidgetLayout.ScoreWidthFraction := ReadFloat(SectionList, 'ScoreWidthFractionOfLane', 0.36); + Sing.PlayerWidgetLayout.OscilloscopeGapBaseY := ReadInteger(SectionList, 'OscilloscopeGapY', 4); + Sing.PlayerWidgetLayout.OscilloscopeGapMinY := ReadInteger(SectionList, 'OscilloscopeGapMinY', 2); + Sing.PlayerWidgetLayout.OscilloscopeMinW := ReadInteger(SectionList, 'OscilloscopeMinW', 40); + Sing.PlayerWidgetLayout.OscilloscopeMinH := ReadInteger(SectionList, 'OscilloscopeMinH', 8); + Sing.PlayerWidgetLayout.PopupYOffsetSolo := ReadInteger(SectionList, 'PopupYOffsetSolo', 65); + Sing.PlayerWidgetLayout.PopupYOffsetDuet := ReadInteger(SectionList, 'PopupYOffsetDuet', 40); + Sing.PlayerWidgetLayout.PopupFontSizeSolo := ReadInteger(SectionList, 'PopupFontSizeSolo', 18); + Sing.PlayerWidgetLayout.PopupFontSizeDuet := ReadInteger(SectionList, 'PopupFontSizeDuet', 14); + + ThemeLoadStatic(Sing.StaticSongName, 'SingSongNameStatic'); + ThemeLoadText(Sing.TextSongName, 'SingSongNameText'); + //Line Bonus Texts Sing.LineBonusText[0] := Language.Translate('POPUP_AWFUL'); Sing.LineBonusText[1] := Sing.LineBonusText[0]; @@ -1751,18 +2251,7 @@ function TTheme.LoadTheme(ThemeNum: integer; sColor: integer): boolean; ThemeLoadStatic(Sing.PausePopUp, 'PausePopUpStatic'); // Score - ThemeLoadBasic(Score, 'Score'); - - ThemeLoadText(Score.TextArtist, 'ScoreTextArtist'); - ThemeLoadText(Score.TextTitle, 'ScoreTextTitle'); - ThemeLoadText(Score.TextArtistTitle, 'ScoreTextArtistTitle'); - - // Send Button's - for I := 1 to 3 do - ThemeLoadButton(Score.ButtonSend[I], 'ScoreButtonSend' + IntToStr(I)); - - ThemeLoadStatic(Score.StaticNavigate, 'ScoreStaticNavigate'); - ThemeLoadText(Score.TextNavigate, 'ScoreTextNavigate'); + ThemeScoreLoadData; // Top5 ThemeLoadBasic(Top5, 'Top5'); @@ -3085,7 +3574,14 @@ function GetSystemColor(Color: integer): TRGB; end; end; -function GetPlayerColor(Color: integer): TRGB; +function BlendColors(const LeftColor, RightColor: TRGB): TRGB; +begin + Result.R := (LeftColor.R + RightColor.R) / 2; + Result.G := (LeftColor.G + RightColor.G) / 2; + Result.B := (LeftColor.B + RightColor.B) / 2; +end; + +function GetBasePlayerColor(Color: integer): TRGB; begin case (Color) of 1://blue @@ -3193,7 +3689,18 @@ function GetPlayerColor(Color: integer): TRGB; end; end; -function GetPlayerLightColor(Color: integer): TRGB; +function GetPlayerColor(Color: integer): TRGB; +var + LeftColor: integer; + RightColor: integer; +begin + if TryGetMixedPlayerColorPair(Color, LeftColor, RightColor) then + Result := BlendColors(GetBasePlayerColor(LeftColor), GetBasePlayerColor(RightColor)) + else + Result := GetBasePlayerColor(Color); +end; + +function GetBasePlayerLightColor(Color: integer): TRGB; begin case (Color) of 1://blue @@ -3301,6 +3808,17 @@ function GetPlayerLightColor(Color: integer): TRGB; end; end; +function GetPlayerLightColor(Color: integer): TRGB; +var + LeftColor: integer; + RightColor: integer; +begin + if TryGetMixedPlayerColorPair(Color, LeftColor, RightColor) then + Result := BlendColors(GetBasePlayerLightColor(LeftColor), GetBasePlayerLightColor(RightColor)) + else + Result := GetBasePlayerLightColor(Color); +end; + function ColorSqrt(RGB: TRGB): TRGB; begin Result.R := sqrt(RGB.R); @@ -3708,64 +4226,100 @@ procedure TTheme.ThemePartyLoad; CloseFile; end; -procedure TTheme.ThemeScoreLoad; +procedure TTheme.ThemeScoreLoadData; var I: integer; - prefix: string; + ScreenCount: integer; + LayoutPlayerCount: integer; + LayoutPlayerIndex: integer; + SectionList: TThemeSectionList; + BaseTemplate: TScoreSlotThemeTemplate; + BaseBounds: TPlayerSlotRect; + LayoutBounds: TPlayerSlotRect; begin - - OpenFile(Ini.Theme); - // Score ThemeLoadBasic(Score, 'Score'); ThemeLoadText(Score.TextArtist, 'ScoreTextArtist'); ThemeLoadText(Score.TextTitle, 'ScoreTextTitle'); ThemeLoadText(Score.TextArtistTitle, 'ScoreTextArtistTitle'); - - if (Ini.Players < 3) or (Ini.Screens = 1) then - prefix := '' - else + SectionList := GetSectionList('ScorePlayerSlotArea'); + if Length(SectionList) = 0 then + SectionList := GetSectionList('ScorePlayerLayout'); + Score.PlayerLayoutArea.X := ReadInteger(SectionList, 'AreaX', ReadInteger(SectionList, 'X', 20)); + Score.PlayerLayoutArea.Y := ReadInteger(SectionList, 'AreaY', ReadInteger(SectionList, 'Y', 110)); + Score.PlayerLayoutArea.W := ReadInteger(SectionList, 'AreaW', ReadInteger(SectionList, 'W', 760)); + Score.PlayerLayoutArea.H := ReadInteger(SectionList, 'AreaH', ReadInteger(SectionList, 'H', 420)); + Score.PlayerLayoutExtraColsBias := ReadInteger(SectionList, 'ExtraColsBias', 0); + + for I := 1 to UIni.IMaxPlayerCount do begin - // 4 players 1 screen - if (Ini.Players = 3) then - prefix := 'FourP'; - - // 6 players 1 screen - if (Ini.Players = 4) then - prefix := 'SixP'; + SetLength(Score.PlayerStatic[I], 0); + SetLength(Score.PlayerTexts[I], 0); + Score.TextName[I] := Default(TThemeText); + Score.TextScore[I] := Default(TThemeText); + Score.AvatarStatic[I] := Default(TThemeStatic); + Score.TextNotes[I] := Default(TThemeText); + Score.TextNotesScore[I] := Default(TThemeText); + Score.TextLineBonus[I] := Default(TThemeText); + Score.TextLineBonusScore[I] := Default(TThemeText); + Score.TextGoldenNotes[I] := Default(TThemeText); + Score.TextGoldenNotesScore[I] := Default(TThemeText); + Score.TextTotal[I] := Default(TThemeText); + Score.TextTotalScore[I] := Default(TThemeText); + Score.StaticBoxLightest[I] := Default(TThemeStatic); + Score.StaticBoxLight[I] := Default(TThemeStatic); + Score.StaticBoxDark[I] := Default(TThemeStatic); + Score.StaticRatings[I] := Default(TThemeStatic); + Score.StaticBackLevel[I] := Default(TThemeStatic); + Score.StaticBackLevelRound[I] := Default(TThemeStatic); + Score.StaticLevel[I] := Default(TThemeStatic); + Score.StaticLevelRound[I] := Default(TThemeStatic); end; - for I := 1 to 6 do - begin - ThemeLoadStatics(Score.PlayerStatic[I], 'Score' + prefix + 'Player' + IntToStr(I) + 'Static'); - ThemeLoadTexts(Score.PlayerTexts[I], 'Score' + prefix + 'Player' + IntToStr(I) + 'Text'); - ThemeLoadStatic(Score.AvatarStatic[I], 'Score' + prefix + 'Player' + IntToStr(I) + 'Avatar'); - - ThemeLoadText(Score.TextName[I], 'Score' + prefix + 'TextName' + IntToStr(I)); - ThemeLoadText(Score.TextScore[I], 'Score' + prefix + 'TextScore' + IntToStr(I)); - ThemeLoadText(Score.TextNotes[I], 'Score' + prefix + 'TextNotes' + IntToStr(I)); - ThemeLoadText(Score.TextNotesScore[I], 'Score' + prefix + 'TextNotesScore' + IntToStr(I)); - ThemeLoadText(Score.TextLineBonus[I], 'Score' + prefix + 'TextLineBonus' + IntToStr(I)); - ThemeLoadText(Score.TextLineBonusScore[I], 'Score' + prefix + 'TextLineBonusScore' + IntToStr(I)); - ThemeLoadText(Score.TextGoldenNotes[I], 'Score' + prefix + 'TextGoldenNotes' + IntToStr(I)); - ThemeLoadText(Score.TextGoldenNotesScore[I], 'Score' + prefix + 'TextGoldenNotesScore' + IntToStr(I)); - ThemeLoadText(Score.TextTotal[I], 'Score' + prefix + 'TextTotal' + IntToStr(I)); - ThemeLoadText(Score.TextTotalScore[I], 'Score' + prefix + 'TextTotalScore' + IntToStr(I)); - - ThemeLoadStatic(Score.StaticBoxLightest[I], 'Score' + prefix + 'StaticBoxLightest' + IntToStr(I)); - ThemeLoadStatic(Score.StaticBoxLight[I], 'Score' + prefix + 'StaticBoxLight' + IntToStr(I)); - ThemeLoadStatic(Score.StaticBoxDark[I], 'Score' + prefix + 'StaticBoxDark' + IntToStr(I)); - - ThemeLoadStatic(Score.StaticBackLevel[I], 'Score' + prefix + 'StaticBackLevel' + IntToStr(I)); - ThemeLoadStatic(Score.StaticBackLevelRound[I], 'Score' + prefix + 'StaticBackLevelRound' + IntToStr(I)); - ThemeLoadStatic(Score.StaticLevel[I], 'Score' + prefix + 'StaticLevel' + IntToStr(I)); - ThemeLoadStatic(Score.StaticLevelRound[I], 'Score' + prefix + 'StaticLevelRound' + IntToStr(I)); - ThemeLoadStatic(Score.StaticRatings[I], 'Score' + prefix + 'StaticRatingPicture' + IntToStr(I)); - end; + ThemeLoadStatics(BaseTemplate.PlayerStatic, 'ScorePlayerTemplateStatic'); + ThemeLoadTexts(BaseTemplate.PlayerTexts, 'ScorePlayerTemplateText'); + ThemeLoadStatic(BaseTemplate.AvatarStatic, 'ScorePlayerTemplateAvatar'); + ThemeLoadText(BaseTemplate.TextName, 'ScorePlayerTemplateTextName'); + ThemeLoadText(BaseTemplate.TextScore, 'ScorePlayerTemplateTextScore'); + ThemeLoadText(BaseTemplate.TextNotes, 'ScorePlayerTemplateTextNotes'); + ThemeLoadText(BaseTemplate.TextNotesScore, 'ScorePlayerTemplateTextNotesScore'); + ThemeLoadText(BaseTemplate.TextLineBonus, 'ScorePlayerTemplateTextLineBonus'); + ThemeLoadText(BaseTemplate.TextLineBonusScore, 'ScorePlayerTemplateTextLineBonusScore'); + ThemeLoadText(BaseTemplate.TextGoldenNotes, 'ScorePlayerTemplateTextGoldenNotes'); + ThemeLoadText(BaseTemplate.TextGoldenNotesScore, 'ScorePlayerTemplateTextGoldenNotesScore'); + ThemeLoadText(BaseTemplate.TextTotal, 'ScorePlayerTemplateTextTotal'); + ThemeLoadText(BaseTemplate.TextTotalScore, 'ScorePlayerTemplateTextTotalScore'); + ThemeLoadStatic(BaseTemplate.StaticBoxLightest, 'ScorePlayerTemplateStaticBoxLightest'); + ThemeLoadStatic(BaseTemplate.StaticBoxLight, 'ScorePlayerTemplateStaticBoxLight'); + ThemeLoadStatic(BaseTemplate.StaticBoxDark, 'ScorePlayerTemplateStaticBoxDark'); + ThemeLoadStatic(BaseTemplate.StaticBackLevel, 'ScorePlayerTemplateStaticBackLevel'); + ThemeLoadStatic(BaseTemplate.StaticBackLevelRound, 'ScorePlayerTemplateStaticBackLevelRound'); + ThemeLoadStatic(BaseTemplate.StaticLevel, 'ScorePlayerTemplateStaticLevel'); + ThemeLoadStatic(BaseTemplate.StaticLevelRound, 'ScorePlayerTemplateStaticLevelRound'); + ThemeLoadStatic(BaseTemplate.StaticRatings, 'ScorePlayerTemplateStaticRatingPicture'); + for I := 1 to High(Score.ButtonSend) do + ThemeLoadButton(Score.ButtonSend[I], 'ScoreButtonSend' + IntToStr(I)); + ThemeLoadStatic(Score.StaticNavigate, 'ScoreStaticNavigate'); + ThemeLoadText(Score.TextNavigate, 'ScoreTextNavigate'); + + BaseBounds := GetScoreTemplateBounds(BaseTemplate); + + ScreenCount := Ini.Screens + 1; + if ScreenCount > 1 then + LayoutPlayerCount := GetScreenPlayerCount(UIni.IPlayersVals[Ini.Players], ScreenCount, 1) + else + LayoutPlayerCount := UIni.IPlayersVals[Ini.Players]; + LayoutBounds := GetThemePlayerScoreLayoutBounds(BaseBounds, Score.PlayerLayoutArea, LayoutPlayerCount); + for LayoutPlayerIndex := 0 to LayoutPlayerCount - 1 do + AssignScoreThemeSlot(Score, BaseTemplate, BaseBounds, LayoutBounds, LayoutPlayerCount, LayoutPlayerIndex, LayoutPlayerIndex + 1); +end; +procedure TTheme.ThemeScoreLoad; +begin + OpenFile(Ini.Theme); + ThemeScoreLoadData; CloseFile; - end; procedure TTheme.ThemeSongLoad; @@ -3831,7 +4385,7 @@ procedure TTheme.ThemeSongLoad; Song.Cover.H := ReadInteger(SectionList, 'H', 200); // Song menu modes: 0 - roulette, 1 - chessboard, 2 - list - + if (TSongMenuMode(Ini.SongMenu) = smChessboard) then begin Song.Cover.Rows := ReadInteger(SectionList, 'Rows', 4); @@ -3925,17 +4479,11 @@ procedure TTheme.ThemeSongLoad; // Duet Singers ThemeLoadStatic (Song.Static2PlayersDuetSingerP1, 'Song' + prefix + 'Static2PlayersDuetSingerP1'); ThemeLoadStatic (Song.Static2PlayersDuetSingerP2, 'Song' + prefix + 'Static2PlayersDuetSingerP2'); - ThemeLoadText (Song.Text2PlayersDuetSingerP1, 'Song' + prefix + 'Text2PlayersDuetSingerP1'); - ThemeLoadText (Song.Text2PlayersDuetSingerP2, 'Song' + prefix + 'Text2PlayersDuetSingerP2'); ThemeLoadStatic (Song.Static3PlayersDuetSingerP1, 'Song' + prefix + 'Static3PlayersDuetSingerP1'); ThemeLoadStatic (Song.Static3PlayersDuetSingerP2, 'Song' + prefix + 'Static3PlayersDuetSingerP2'); ThemeLoadStatic (Song.Static3PlayersDuetSingerP3, 'Song' + prefix + 'Static3PlayersDuetSingerP3'); - ThemeLoadText (Song.Text3PlayersDuetSingerP1, 'Song' + prefix + 'Text3PlayersDuetSingerP1'); - ThemeLoadText (Song.Text3PlayersDuetSingerP2, 'Song' + prefix + 'Text3PlayersDuetSingerP2'); - ThemeLoadText (Song.Text3PlayersDuetSingerP3, 'Song' + prefix + 'Text3PlayersDuetSingerP3'); - // 4/6 players 1 screen ThemeLoadStatic (Song.Static4PlayersDuetSingerP3, 'Song' + prefix + 'Static4PlayersDuetSingerP3'); ThemeLoadStatic (Song.Static4PlayersDuetSingerP4, 'Song' + prefix + 'Static4PlayersDuetSingerP4'); @@ -3943,6 +4491,19 @@ procedure TTheme.ThemeSongLoad; ThemeLoadStatic (Song.Static6PlayersDuetSingerP5, 'Song' + prefix + 'Static6PlayersDuetSingerP5'); ThemeLoadStatic (Song.Static6PlayersDuetSingerP6, 'Song' + prefix + 'Static6PlayersDuetSingerP6'); + ThemeLoadText (Song.Text2PlayersDuetSingerP1, 'Song' + prefix + 'Text2PlayersDuetSingerP1'); + ThemeLoadText (Song.Text2PlayersDuetSingerP2, 'Song' + prefix + 'Text2PlayersDuetSingerP2'); + + ThemeLoadText (Song.Text3PlayersDuetSingerP1, 'Song' + prefix + 'Text3PlayersDuetSingerP1'); + ThemeLoadText (Song.Text3PlayersDuetSingerP2, 'Song' + prefix + 'Text3PlayersDuetSingerP2'); + ThemeLoadText (Song.Text3PlayersDuetSingerP3, 'Song' + prefix + 'Text3PlayersDuetSingerP3'); + + SectionList := GetSectionList('Song' + prefix + 'DuetSingerArea'); + Song.DuetSingerArea.X := ReadInteger(SectionList, 'X', Song.Static2PlayersDuetSingerP1.X); + Song.DuetSingerArea.Y := ReadInteger(SectionList, 'Y', Song.Static2PlayersDuetSingerP1.Y); + Song.DuetSingerArea.W := ReadInteger(SectionList, 'W', Song.Static2PlayersDuetSingerP1.W); + Song.DuetSingerArea.H := ReadInteger(SectionList, 'H', Song.Static2PlayersDuetSingerP1.H); + //Party Mode ThemeLoadStatic(Song.StaticTeam1Joker1, 'Song' + prefix + 'StaticTeam1Joker1'); ThemeLoadStatic(Song.StaticTeam1Joker2, 'Song' + prefix + 'StaticTeam1Joker2'); From eaf32a8c2edcd6b0cd1ce9a43a4f8ed82a1d1b8b Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:30:22 +0200 Subject: [PATCH 05/11] Use generic layout for player selection screens --- game/themes/Modern.ini | 6 +- src/base/UIni.pas | 41 +++ src/screens/UScreenName.pas | 248 +++++++++++++++---- src/screens/UScreenPartyPlayer.pas | 59 ++--- src/screens/UScreenPartyTournamentPlayer.pas | 145 ++--------- 5 files changed, 283 insertions(+), 216 deletions(-) diff --git a/game/themes/Modern.ini b/game/themes/Modern.ini index 5288da034..9fe9da192 100644 --- a/game/themes/Modern.ini +++ b/game/themes/Modern.ini @@ -3219,11 +3219,11 @@ Inherits = NamePlayerSelectStatic1Avatar X = 635 [NamePlayerSelectCurrent] -X = -10 +X = 90 Y = 175 W = 90 -H = 100 -Z = 0.95 +H = 90 +Z = 0 Tex = SelectFrame Color = ColorLight DColor = ColorDark diff --git a/src/base/UIni.pas b/src/base/UIni.pas index 0387fd862..7a713978e 100644 --- a/src/base/UIni.pas +++ b/src/base/UIni.pas @@ -93,6 +93,12 @@ TInputDeviceConfig = record IPlayers: array[0..4] of UTF8String = ('1', '2', '3', '4', '6'); IPlayersVals: array[0..4] of integer = ( 1 , 2 , 3 , 4 , 6 ); +type + TUTF8StringArray = array of UTF8String; + TIntegerArray = array of integer; + +function GetNameTemplateIndexFromKey(const Key: cardinal): integer; +function CreateNumericOptionArray(const FirstValue, LastValue: integer): TUTF8StringArray; function TryGetMixedPlayerColorPair(ColorIndex: integer; out LeftColor, RightColor: integer): boolean; type @@ -620,6 +626,41 @@ TSafeIniFile = class(TIniFile) IGNORE_INDEX = -1; BASE_PLAYER_COLOR_COUNT = 16; +function GetNameTemplateIndexFromKey(const Key: cardinal): integer; +begin + case Key of + SDLK_F1: Result := 0; + SDLK_F2: Result := 1; + SDLK_F3: Result := 2; + SDLK_F4: Result := 3; + SDLK_F5: Result := 4; + SDLK_F6: Result := 5; + SDLK_F7: Result := 6; + SDLK_F8: Result := 7; + SDLK_F9: Result := 8; + SDLK_F10: Result := 9; + SDLK_F11: Result := 10; + SDLK_F12: Result := 11; + else + Result := -1; + end; +end; + +function CreateNumericOptionArray(const FirstValue, LastValue: integer): TUTF8StringArray; +var + I: integer; +begin + if LastValue < FirstValue then + begin + SetLength(Result, 0); + Exit; + end; + + SetLength(Result, LastValue - FirstValue + 1); + for I := 0 to High(Result) do + Result[I] := IntToStr(FirstValue + I); +end; + function TryGetMixedPlayerColorPair(ColorIndex: integer; out LeftColor, RightColor: integer): boolean; var Remaining: integer; diff --git a/src/screens/UScreenName.pas b/src/screens/UScreenName.pas index 43193cab1..d2a192d80 100644 --- a/src/screens/UScreenName.pas +++ b/src/screens/UScreenName.pas @@ -55,11 +55,16 @@ interface TScreenName = class(TMenu) private PlayersCount: cardinal; + PlayersCountIID: integer; PlayerAvatar: cardinal; PlayerName: cardinal; + PlayerNameIID: integer; PlayerColor: cardinal; + PlayerColorIID: integer; PlayerSelect: cardinal; + PlayerSelectIID: integer; PlayerSelectLevel: cardinal; + PlayerSelectLevelIID: integer; CountIndex: integer; PlayerIndex: integer; @@ -90,7 +95,7 @@ TScreenName = class(TMenu) PlayerAvatarButtonMD5: array of UTF8String; public Goto_SingScreen: boolean; //If true then next Screen in SingScreen - + constructor Create; override; function ShouldHandleInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean; out SuppressKey: boolean): boolean; override; function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; @@ -114,6 +119,7 @@ TScreenName = class(TMenu) procedure GenerateAvatars(); procedure SetPlayerAvatar(Player: integer); + procedure ApplyPlayerSelectLayout(Player, PlayerCount: integer); end; var @@ -134,12 +140,54 @@ implementation UMain, UMenuButton, UPath, + UPlayerLayout, USkins, USongs, UTime, UUnicodeUtils, Math; +function GetNamePlayerSelectBaseBounds: TPlayerSlotRect; +begin + Result := UThemes.GetNamePlayerSelectBaseBounds(Theme.Name); +end; + +function GetNamePlayerSelectLayoutBounds(const BaseBounds: TPlayerSlotRect; PlayerCount: integer): TPlayerSlotRect; +begin + Result := UThemes.GetNamePlayerSelectLayoutBounds(BaseBounds, Theme.Name.PlayerSelectLayout); +end; + +function GetNamePlayerSelectSlotRect(PlayerIndex, PlayerCount: integer; const BaseBounds, LayoutBounds: TPlayerSlotRect): TPlayerSlotRect; +begin + Result := UThemes.GetNamePlayerSelectSlotRect(PlayerIndex, PlayerCount, BaseBounds, LayoutBounds, Theme.Name.PlayerSelectLayout); +end; + +function ScaleNameCoord(const Value, SourceStart, SourceSize, TargetStart, TargetSize: integer): integer; +begin + Result := ScaleCoordToSlot(Value, SourceStart, SourceSize, TargetStart, TargetSize); +end; + +function ScaleNameLength(const Value, SourceSize, TargetSize: integer): integer; +begin + Result := ScaleLengthToSlot(Value, SourceSize, TargetSize); +end; + +function GetNamePlayerSelectFittedRect(PlayerIndex, PlayerCount: integer): TPlayerSlotRect; +var + BaseBounds: TPlayerSlotRect; + LayoutBounds: TPlayerSlotRect; + SlotRect: TPlayerSlotRect; + MaxScale: real; +begin + BaseBounds := GetNamePlayerSelectBaseBounds; + LayoutBounds := GetNamePlayerSelectLayoutBounds(BaseBounds, PlayerCount); + SlotRect := GetNamePlayerSelectSlotRect(PlayerIndex, PlayerCount, BaseBounds, LayoutBounds); + + MaxScale := UThemes.GetNamePlayerSelectMaxScale(PlayerCount, Theme.Name.PlayerSelectLayout); + + Result := GetFittedSlotRect(BaseBounds, SlotRect, MaxScale); +end; + function TScreenName.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; var I: integer; @@ -183,7 +231,7 @@ function TScreenName.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: in if InRegion(X, Y, Button[PlayerAvatarButton[Btn]].GetMouseOverArea) then begin - Interaction := 2; + Interaction := PlayerAvatarIID; ParseInput(SDLK_LEFT, 0, true); ParseInput(SDLK_LEFT, 0, true); @@ -196,7 +244,7 @@ function TScreenName.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: in if InRegion(X, Y, Button[PlayerAvatarButton[Btn]].GetMouseOverArea) then begin - Interaction := 2; + Interaction := PlayerAvatarIID; ParseInput(SDLK_LEFT, 0, true); end; @@ -208,7 +256,7 @@ function TScreenName.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: in if InRegion(X, Y, Button[PlayerAvatarButton[Btn]].GetMouseOverArea) then begin - Interaction := 2; + Interaction := PlayerAvatarIID; ParseInput(SDLK_RIGHT, 0, true); end; @@ -220,17 +268,18 @@ function TScreenName.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: in if InRegion(X, Y, Button[PlayerAvatarButton[Btn]].GetMouseOverArea) then begin - Interaction := 2; + Interaction := PlayerAvatarIID; ParseInput(SDLK_RIGHT, 0, true); ParseInput(SDLK_RIGHT, 0, true); end; // click for change player profile - for I := 0 to 5 do + for I := 0 to High(PlayerCurrent) do begin if Statics[PlayerCurrent[I]].Visible and InRegion(X, Y, Statics[PlayerCurrent[I]].GetMouseOverArea) then begin + Interaction := PlayerSelectIID; PlayerIndex := I; RefreshProfile(); @@ -247,18 +296,11 @@ function TScreenName.ShouldHandleInput(PressedKey: cardinal; CharCode: UCS4Char; begin Result := inherited; // only suppress special keys for now - case PressedKey of - // Templates for Names Mod - SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12: - if (Button[PlayerName].Selected) then - begin - SuppressKey := true; - end - else - begin - Result := false; - end; - end; + if GetNameTemplateIndexFromKey(PressedKey) <> -1 then + if (Button[PlayerName].Selected) then + SuppressKey := true + else + Result := false; end; function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; @@ -285,6 +327,15 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse end; end; + procedure HandleTemplateHotkey; + var + TemplateIndex: integer; + begin + TemplateIndex := GetNameTemplateIndexFromKey(PressedKey); + if (TemplateIndex >= 0) and (TemplateIndex <= High(Ini.NameTemplate)) then + HandleNameTemplate(TemplateIndex); + end; + begin Result := true; if (PressedDown) then @@ -304,7 +355,7 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse end; end; end - else if (Interaction = 3) and (IsPrintableChar(CharCode)) then + else if (Interaction = PlayerNameIID) and (IsPrintableChar(CharCode)) then begin // pass printable chars to button Button[PlayerName].Text[0].Text := Button[PlayerName].Text[0].Text + @@ -317,23 +368,13 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse // check special keys case PressedKey of - // Templates for Names Mod - SDLK_F1: HandleNameTemplate(0); - SDLK_F2: HandleNameTemplate(1); - SDLK_F3: HandleNameTemplate(2); - SDLK_F4: HandleNameTemplate(3); - SDLK_F5: HandleNameTemplate(4); - SDLK_F6: HandleNameTemplate(5); - SDLK_F7: HandleNameTemplate(6); - SDLK_F8: HandleNameTemplate(7); - SDLK_F9: HandleNameTemplate(8); - SDLK_F10: HandleNameTemplate(9); - SDLK_F11: HandleNameTemplate(10); - SDLK_F12: HandleNameTemplate(11); + SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, + SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12: + HandleTemplateHotkey; SDLK_BACKSPACE: begin - if (Interaction = 3) then + if (Interaction = PlayerNameIID) then begin Button[PlayerName].Text[0].DeleteLastLetter(); PlayerNames[PlayerIndex] := Button[PlayerName].Text[0].Text; @@ -449,13 +490,13 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse begin AudioPlayback.PlaySound(SoundLib.Change); - if (Interaction in [0, 4, 5]) then + if (Interaction in [PlayersCountIID, PlayerColorIID, PlayerSelectLevelIID]) then InteractInc; - if (Interaction = 0) then + if (Interaction = PlayersCountIID) then RefreshPlayers(); - if (Interaction = 1) then + if (Interaction = PlayerSelectIID) then begin //TODO: adapt this to new playersize if (PlayerIndex < UIni.IPlayersVals[CountIndex]-1) then begin @@ -468,7 +509,7 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse end; end; - if (Interaction = 2) then + if (Interaction = PlayerAvatarIID) then begin SelectNext; SetAvatarScroll; @@ -476,13 +517,13 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse SetPlayerAvatar(PlayerIndex); end; - if (Interaction = 4) then + if (Interaction = PlayerColorIID) then begin RefreshColor(); SelectsS[PlayerColor].SetSelect(true); end; - if (Interaction = 5) then + if (Interaction = PlayerSelectLevelIID) then begin PlayerLevel[PlayerIndex] := LevelIndex; end; @@ -492,13 +533,13 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse begin AudioPlayback.PlaySound(SoundLib.Change); - if (Interaction in [0, 4, 5]) then + if (Interaction in [PlayersCountIID, PlayerColorIID, PlayerSelectLevelIID]) then InteractDec; - if (Interaction = 0) then + if (Interaction = PlayersCountIID) then RefreshPlayers(); - if (Interaction = 1) then + if (Interaction = PlayerSelectIID) then begin if (PlayerIndex > 0) then begin @@ -511,7 +552,7 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse end; end; - if (Interaction = 2) then + if (Interaction = PlayerAvatarIID) then begin SelectPrev; SetAvatarScroll; @@ -519,16 +560,17 @@ function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; Presse SetPlayerAvatar(PlayerIndex); end; - if (Interaction = 4) then + if (Interaction = PlayerColorIID) then begin RefreshColor(); SelectsS[PlayerColor].SetSelect(true); end; - if (Interaction = 5) then + if (Interaction = PlayerSelectLevelIID) then begin PlayerLevel[PlayerIndex] := LevelIndex; end; + end; end; @@ -542,15 +584,27 @@ procedure TScreenName.GenerateAvatars(); Avatar: TAvatar; AvatarFile: IPath; Hash: string; + GenericNoAvatarFile: IPath; + NoAvatarFile: IPath; begin SetLength(PlayerAvatarButton, Length(AvatarsList) + 1); SetLength(PlayerAvatarButtonMD5, Length(AvatarsList) + 1); // 1st no-avatar dummy + GenericNoAvatarFile := Skin.SkinPath.Append(Path('[name]noavatar.png')); for I := 1 to UIni.IMaxPlayerCount do begin - NoAvatarTexture[I] := Texture.GetTexture(Skin.GetTextureFileName('NoAvatar_P' + IntToStr(I)), TEXTURE_TYPE_TRANSPARENT, $FFFFFF); + if GenericNoAvatarFile.IsFile() then + NoAvatarFile := GenericNoAvatarFile + else + begin + NoAvatarFile := Skin.GetTextureFileName('NoAvatar_P' + IntToStr(I)); + if not NoAvatarFile.IsSet then + NoAvatarFile := Skin.GetTextureFileName('NoAvatar_P1'); + end; + + NoAvatarTexture[I] := Texture.GetTexture(NoAvatarFile, TEXTURE_TYPE_TRANSPARENT, $FFFFFF); end; // create no-avatar @@ -592,8 +646,69 @@ procedure TScreenName.GenerateAvatars(); end; procedure TScreenName.ChangeSelectPlayerPosition(Player: integer); +var + BaseBounds: TPlayerSlotRect; + Count: integer; + FittedRect: TPlayerSlotRect; + SelectButton: integer; +begin + Count := UIni.IPlayersVals[CountIndex]; + BaseBounds := GetNamePlayerSelectBaseBounds; + FittedRect := GetNamePlayerSelectFittedRect(Player, Count); + + Button[PlayerSelect].X := ScaleNameCoord( + Theme.Name.PlayerSelectCurrent.X, BaseBounds.X, BaseBounds.W, FittedRect.X, FittedRect.W); + Button[PlayerSelect].Y := ScaleNameCoord( + Theme.Name.PlayerSelectCurrent.Y, BaseBounds.Y, BaseBounds.H, FittedRect.Y, FittedRect.H); + Button[PlayerSelect].W := ScaleNameLength( + Theme.Name.PlayerSelectCurrent.W, BaseBounds.W, FittedRect.W); + Button[PlayerSelect].H := ScaleNameLength( + Theme.Name.PlayerSelectCurrent.H, BaseBounds.H, FittedRect.H); + Button[PlayerSelect].SelectW := Button[PlayerSelect].W; + Button[PlayerSelect].SelectH := Button[PlayerSelect].H; + + SelectButton := Interactions[PlayerSelectIID].Num; + Button[SelectButton].X := Button[PlayerSelect].X; + Button[SelectButton].Y := Button[PlayerSelect].Y; + Button[SelectButton].W := Button[PlayerSelect].W; + Button[SelectButton].H := Button[PlayerSelect].H; + Button[SelectButton].SelectW := Button[PlayerSelect].SelectW; + Button[SelectButton].SelectH := Button[PlayerSelect].SelectH; +end; + +procedure TScreenName.ApplyPlayerSelectLayout(Player, PlayerCount: integer); +var + BaseBounds: TPlayerSlotRect; + FittedRect: TPlayerSlotRect; begin - Button[PlayerSelect].X := Theme.Name.PlayerSelect[Player].X + Theme.Name.PlayerSelectCurrent.X; + BaseBounds := GetNamePlayerSelectBaseBounds; + FittedRect := GetNamePlayerSelectFittedRect(Player, PlayerCount); + + Statics[PlayerCurrent[Player]].Texture.X := ScaleNameCoord( + Theme.Name.PlayerSelectTemplateFrame.X, BaseBounds.X, BaseBounds.W, FittedRect.X, FittedRect.W); + Statics[PlayerCurrent[Player]].Texture.Y := ScaleNameCoord( + Theme.Name.PlayerSelectTemplateFrame.Y, BaseBounds.Y, BaseBounds.H, FittedRect.Y, FittedRect.H); + Statics[PlayerCurrent[Player]].Texture.W := ScaleNameLength( + Theme.Name.PlayerSelectTemplateFrame.W, BaseBounds.W, FittedRect.W); + Statics[PlayerCurrent[Player]].Texture.H := ScaleNameLength( + Theme.Name.PlayerSelectTemplateFrame.H, BaseBounds.H, FittedRect.H); + + Text[PlayerCurrentText[Player]].X := ScaleNameCoord( + Theme.Name.PlayerSelectTemplateText.X, BaseBounds.X, BaseBounds.W, FittedRect.X, FittedRect.W); + Text[PlayerCurrentText[Player]].Y := ScaleNameCoord( + Theme.Name.PlayerSelectTemplateText.Y, BaseBounds.Y, BaseBounds.H, FittedRect.Y, FittedRect.H) - + Max(2, ScaleNameLength(4, BaseBounds.H, FittedRect.H)); + Text[PlayerCurrentText[Player]].Size := Max(8, ScaleNameLength( + Theme.Name.PlayerSelectTemplateText.Size, BaseBounds.H, FittedRect.H)); + + Statics[PlayerCurrentAvatar[Player]].Texture.X := ScaleNameCoord( + Theme.Name.PlayerSelectTemplateAvatar.X, BaseBounds.X, BaseBounds.W, FittedRect.X, FittedRect.W); + Statics[PlayerCurrentAvatar[Player]].Texture.Y := ScaleNameCoord( + Theme.Name.PlayerSelectTemplateAvatar.Y, BaseBounds.Y, BaseBounds.H, FittedRect.Y, FittedRect.H); + Statics[PlayerCurrentAvatar[Player]].Texture.W := ScaleNameLength( + Theme.Name.PlayerSelectTemplateAvatar.W, BaseBounds.W, FittedRect.W); + Statics[PlayerCurrentAvatar[Player]].Texture.H := ScaleNameLength( + Theme.Name.PlayerSelectTemplateAvatar.H, BaseBounds.H, FittedRect.H); end; procedure TScreenName.RefreshPlayers(); @@ -608,8 +723,10 @@ procedure TScreenName.RefreshPlayers(); PlayerIndex := PlayerIndex - 1; // Player Colors - for I := Count-1 downto 0 do + for I := 0 to Count-1 do begin + ApplyPlayerSelectLayout(I, Count); + if (Ini.PlayerColor[I] > 0) then Num[I] := NoRepeatColors(Ini.PlayerColor[I], I, 1) else @@ -808,6 +925,7 @@ constructor TScreenName.Create; Theme.Name.SelectPlayersCount.oneItemOnly := true; Theme.Name.SelectPlayersCount.showArrows := true; PlayersCount := AddSelectSlide(Theme.Name.SelectPlayersCount, CountIndex, IPlayers); + PlayersCountIID := High(Interactions); for I := 0 to UIni.IMaxPlayerCount -1 do begin @@ -817,20 +935,31 @@ constructor TScreenName.Create; end; PlayerSelect := AddButton(Theme.Name.PlayerSelectCurrent); + Button[PlayerSelect].Selectable := false; + + AddButton(0, 0, 0, 0, Skin.GetTextureFileName(Theme.Name.PlayerSelectCurrent.Tex), Theme.Name.PlayerSelectCurrent.Typ, false); + PlayerSelectIID := High(Interactions); + Button[Interactions[PlayerSelectIID].Num].SelectInt := 0; + Button[Interactions[PlayerSelectIID].Num].DeselectInt := 0; + Button[Interactions[PlayerSelectIID].Num].Texture.Alpha := 0; + Button[Interactions[PlayerSelectIID].Num].DeSelectTexture.Alpha := 0; PlayerAvatar := AddButton(Theme.Name.PlayerButtonAvatar); PlayerAvatarIID := High(Interactions); PlayerName := AddButton(Theme.Name.PlayerButtonName); + PlayerNameIID := High(Interactions); Button[PlayerName].Text[0].Writable := true; Theme.Name.SelectPlayerColor.oneItemOnly := true; Theme.Name.SelectPlayerColor.showArrows := true; PlayerColor := AddSelectSlide(Theme.Name.SelectPlayerColor, ColorIndex, IPlayerColorTranslated); + PlayerColorIID := High(Interactions); Theme.Name.SelectPlayerLevel.oneItemOnly := true; Theme.Name.SelectPlayerLevel.showArrows := true; PlayerSelectLevel := AddSelectSlide(Theme.Name.SelectPlayerLevel, LevelIndex, IDifficultyTranslated); + PlayerSelectLevelIID := High(Interactions); isScrolling := false; @@ -845,7 +974,17 @@ constructor TScreenName.Create; procedure TScreenName.SetPlayerAvatar(Player: integer); var Col: TRGB; + X: real; + Y: real; + W: real; + H: real; + Z: real; begin + X := Statics[PlayerCurrentAvatar[Player]].Texture.X; + Y := Statics[PlayerCurrentAvatar[Player]].Texture.Y; + W := Statics[PlayerCurrentAvatar[Player]].Texture.W; + H := Statics[PlayerCurrentAvatar[Player]].Texture.H; + Z := Statics[PlayerCurrentAvatar[Player]].Texture.Z; if (PlayerAvatars[Player] = 0) then begin @@ -860,12 +999,11 @@ procedure TScreenName.SetPlayerAvatar(Player: integer); else Statics[PlayerCurrentAvatar[Player]].Texture := Button[PlayerAvatarButton[PlayerAvatars[Player]]].Texture; - Statics[PlayerCurrentAvatar[Player]].Texture.X := Theme.Name.PlayerSelectAvatar[Player].X; - Statics[PlayerCurrentAvatar[Player]].Texture.Y := Theme.Name.PlayerSelectAvatar[Player].Y; - Statics[PlayerCurrentAvatar[Player]].Texture.W := Theme.Name.PlayerSelectAvatar[Player].W; - Statics[PlayerCurrentAvatar[Player]].Texture.H := Theme.Name.PlayerSelectAvatar[Player].H; - Statics[PlayerCurrentAvatar[Player]].Texture.Z := Theme.Name.PlayerSelectAvatar[Player].Z; - + Statics[PlayerCurrentAvatar[Player]].Texture.X := X; + Statics[PlayerCurrentAvatar[Player]].Texture.Y := Y; + Statics[PlayerCurrentAvatar[Player]].Texture.W := W; + Statics[PlayerCurrentAvatar[Player]].Texture.H := H; + Statics[PlayerCurrentAvatar[Player]].Texture.Z := Z; Statics[PlayerCurrentAvatar[Player]].Texture.Int := 1; end; @@ -994,7 +1132,7 @@ procedure TScreenName.SetAvatarScroll; Button[B].Y := Theme.Name.PlayerAvatar.Y; AvatarCurrent := AvatarTarget; - + isScrolling := false; end diff --git a/src/screens/UScreenPartyPlayer.pas b/src/screens/UScreenPartyPlayer.pas index 8e0e59d51..7270cc4bd 100644 --- a/src/screens/UScreenPartyPlayer.pas +++ b/src/screens/UScreenPartyPlayer.pas @@ -97,10 +97,6 @@ TScreenPartyPlayer = class(TMenu) end; -const - ITeams: array[0..1] of UTF8String = ('2', '3'); - IPlayers: array[0..3] of UTF8String = ('1', '2', '3', '4'); - const ID='ID_031'; //for help system @@ -246,18 +242,11 @@ function TScreenPartyPlayer.ShouldHandleInput(PressedKey: cardinal; CharCode: UC begin Result := inherited; // only suppress special keys for now - case PressedKey of - // Templates for Names Mod - SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12: - if (Button[Interactions[Interaction].Num].Selected) then - begin - SuppressKey := true; - end - else - begin - Result := false; - end; - end; + if GetNameTemplateIndexFromKey(PressedKey) <> -1 then + if (Button[Interactions[Interaction].Num].Selected) then + SuppressKey := true + else + Result := false; end; function TScreenPartyPlayer.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; @@ -294,6 +283,14 @@ function TScreenPartyPlayer.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; if isAlternate then Ini.NameTemplate[index] := Button[Interactions[Interaction].Num].Text[0].Text else Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[index]; end; + procedure HandleTemplateHotkey; + var + TemplateIndex: integer; + begin + TemplateIndex := GetNameTemplateIndexFromKey(PressedKey); + if (TemplateIndex >= 0) and (TemplateIndex <= High(Ini.NameTemplate)) then + HandleNameTemplate(TemplateIndex); + end; begin Result := true; @@ -318,20 +315,9 @@ function TScreenPartyPlayer.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; // check special keys case PressedKey of - // Templates for Names Mod - SDLK_F1: HandleNameTemplate(0); - SDLK_F2: HandleNameTemplate(1); - SDLK_F3: HandleNameTemplate(2); - SDLK_F4: HandleNameTemplate(3); - SDLK_F5: HandleNameTemplate(4); - SDLK_F6: HandleNameTemplate(5); - SDLK_F7: HandleNameTemplate(6); - SDLK_F8: HandleNameTemplate(7); - SDLK_F9: HandleNameTemplate(8); - SDLK_F10: HandleNameTemplate(9); - SDLK_F11: HandleNameTemplate(10); - SDLK_F12: HandleNameTemplate(11); - + SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, + SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12: + HandleTemplateHotkey; SDLK_BACKSPACE: begin Button[Interactions[Interaction].Num].Text[0].DeleteLastLetter; @@ -492,21 +478,26 @@ function TScreenPartyPlayer.ParseMouse(MouseButton: integer; BtnDown: boolean; X constructor TScreenPartyPlayer.Create; var ButtonID: integer; + TeamOptions: TUTF8StringArray; + PlayerOptions: TUTF8StringArray; begin inherited Create; LoadFromTheme(Theme.PartyPlayer); + TeamOptions := CreateNumericOptionArray(2, 3); + PlayerOptions := CreateNumericOptionArray(1, 4); + Theme.PartyPlayer.SelectTeams.oneItemOnly := true; Theme.PartyPlayer.SelectTeams.showArrows := true; - SelectTeams := AddSelectSlide(Theme.PartyPlayer.SelectTeams, CountTeams, ITeams); + SelectTeams := AddSelectSlide(Theme.PartyPlayer.SelectTeams, CountTeams, TeamOptions); Team1Name := AddButton(Theme.PartyPlayer.Team1Name); Button[Team1Name].Text[0].Writable := true; Theme.PartyPlayer.SelectPlayers1.oneItemOnly := true; Theme.PartyPlayer.SelectPlayers1.showArrows := true; - SelectPlayers[0] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers1, CountPlayer[0], IPlayers); + SelectPlayers[0] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers1, CountPlayer[0], PlayerOptions); ButtonID := AddButton(Theme.PartyPlayer.Player1Name); Button[ButtonID].Text[0].Writable := true; @@ -525,7 +516,7 @@ constructor TScreenPartyPlayer.Create; Theme.PartyPlayer.SelectPlayers2.oneItemOnly := true; Theme.PartyPlayer.SelectPlayers2.showArrows := true; - SelectPlayers[1] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers2, CountPlayer[1], IPlayers); + SelectPlayers[1] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers2, CountPlayer[1], PlayerOptions); ButtonID := AddButton(Theme.PartyPlayer.Player5Name); Button[ButtonID].Text[0].Writable := true; @@ -544,7 +535,7 @@ constructor TScreenPartyPlayer.Create; Theme.PartyPlayer.SelectPlayers3.oneItemOnly := true; Theme.PartyPlayer.SelectPlayers3.showArrows := true; - SelectPlayers[2] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers3, CountPlayer[2], IPlayers); + SelectPlayers[2] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers3, CountPlayer[2], PlayerOptions); ButtonID := AddButton(Theme.PartyPlayer.Player9Name); Button[ButtonID].Text[0].Writable := true; diff --git a/src/screens/UScreenPartyTournamentPlayer.pas b/src/screens/UScreenPartyTournamentPlayer.pas index 0d281a9a8..18624ed30 100644 --- a/src/screens/UScreenPartyTournamentPlayer.pas +++ b/src/screens/UScreenPartyTournamentPlayer.pas @@ -80,7 +80,6 @@ TScreenPartyTournamentPlayer = class(TMenu) const ID='ID_036'; //for help system - ITournamentPlayers: array[0..14] of UTF8String = ('2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'); implementation @@ -153,24 +152,18 @@ function TScreenPartyTournamentPlayer.ShouldHandleInput(PressedKey: cardinal; Ch begin Result := inherited; // only suppress special keys for now - case PressedKey of - // Templates for Names Mod - SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12: - if (Button[Interactions[Interaction].Num].Selected) then - begin - SuppressKey := true; - end - else - begin - Result := false; - end; - end; + if GetNameTemplateIndexFromKey(PressedKey) <> -1 then + if (Button[Interactions[Interaction].Num].Selected) then + SuppressKey := true + else + Result := false; end; function TScreenPartyTournamentPlayer.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var SDL_ModState: word; isAlternate: boolean; + TemplateIndex: integer; procedure IntNext; begin repeat @@ -215,116 +208,16 @@ function TScreenPartyTournamentPlayer.ParseInput(PressedKey: cardinal; CharCode: isAlternate := (SDL_ModState = KMOD_LSHIFT) or (SDL_ModState = KMOD_RSHIFT); isAlternate := isAlternate or (SDL_ModState = KMOD_LALT); // legacy key combination case PressedKey of - // Templates for Names Mod - SDLK_F1: - if isAlternate then - begin - Ini.NameTemplate[0] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[0]; - end; - SDLK_F2: - if isAlternate then - begin - Ini.NameTemplate[1] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[1]; - end; - SDLK_F3: - if isAlternate then - begin - Ini.NameTemplate[2] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[2]; - end; - SDLK_F4: - if isAlternate then - begin - Ini.NameTemplate[3] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[3]; - end; - SDLK_F5: - if isAlternate then - begin - Ini.NameTemplate[4] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[4]; - end; - SDLK_F6: - if isAlternate then - begin - Ini.NameTemplate[5] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[5]; - end; - SDLK_F7: - if isAlternate then - begin - Ini.NameTemplate[6] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[6]; - end; - SDLK_F8: - if isAlternate then - begin - Ini.NameTemplate[7] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[7]; - end; - SDLK_F9: - if isAlternate then - begin - Ini.NameTemplate[8] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[8]; - end; - SDLK_F10: - if isAlternate then - begin - Ini.NameTemplate[9] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[9]; - end; - SDLK_F11: - if isAlternate then - begin - Ini.NameTemplate[10] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[10]; - end; - SDLK_F12: - if isAlternate then - begin - Ini.NameTemplate[11] := Button[Interactions[Interaction].Num].Text[0].Text; - end - else - begin - Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[11]; - end; - + SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, + SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12: + begin + TemplateIndex := GetNameTemplateIndexFromKey(PressedKey); + if (TemplateIndex >= 0) and (TemplateIndex <= High(Ini.NameTemplate)) then + if isAlternate then + Ini.NameTemplate[TemplateIndex] := Button[Interactions[Interaction].Num].Text[0].Text + else + Button[Interactions[Interaction].Num].Text[0].Text := Ini.NameTemplate[TemplateIndex]; + end; SDLK_BACKSPACE: begin Button[Interactions[Interaction].Num].Text[0].DeleteLastLetter; @@ -384,14 +277,18 @@ function TScreenPartyTournamentPlayer.ParseInput(PressedKey: cardinal; CharCode: end; constructor TScreenPartyTournamentPlayer.Create; +var + TournamentPlayerOptions: TUTF8StringArray; begin inherited Create; LoadFromTheme(Theme.PartyTournamentPlayer); + TournamentPlayerOptions := CreateNumericOptionArray(2, 16); + Theme.PartyTournamentPlayer.SelectPlayers.oneItemOnly := true; Theme.PartyTournamentPlayer.SelectPlayers.showArrows := true; - SelectPlayers := AddSelectSlide(Theme.PartyTournamentPlayer.SelectPlayers, CountPlayer, ITournamentPlayers); + SelectPlayers := AddSelectSlide(Theme.PartyTournamentPlayer.SelectPlayers, CountPlayer, TournamentPlayerOptions); AddButton(Theme.PartyTournamentPlayer.Player1Name); AddButton(Theme.PartyTournamentPlayer.Player2Name); From 361821997d90f30594f61801c4e17a709ed67f5a Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:30:54 +0200 Subject: [PATCH 06/11] Use generic layout for song duet singers --- src/screens/UScreenSong.pas | 387 ++++++++++++++++-------------------- 1 file changed, 170 insertions(+), 217 deletions(-) diff --git a/src/screens/UScreenSong.pas b/src/screens/UScreenSong.pas index 8aec83681..c91da598e 100644 --- a/src/screens/UScreenSong.pas +++ b/src/screens/UScreenSong.pas @@ -45,6 +45,7 @@ interface UMenu, UMenuEqualizer, UMusic, + UPlayerLayout, UPath, USong, USongs, @@ -195,26 +196,10 @@ TScreenSong = class(TMenu) InfoMessageBG: cardinal; InfoMessageText: cardinal; - Static2PlayersDuetSingerP1: cardinal; - Static2PlayersDuetSingerP2: cardinal; - Text2PlayersDuetSingerP1: cardinal; - Text2PlayersDuetSingerP2: cardinal; - - Static3PlayersDuetSingerP1: cardinal; - Static3PlayersDuetSingerP2: cardinal; - Static3PlayersDuetSingerP3: cardinal; - Text3PlayersDuetSingerP1: cardinal; - Text3PlayersDuetSingerP2: cardinal; - Text3PlayersDuetSingerP3: cardinal; - - Static4PlayersDuetSingerP3: cardinal; - Static4PlayersDuetSingerP4: cardinal; - - Static6PlayersDuetSingerP4: cardinal; - Static6PlayersDuetSingerP5: cardinal; - Static6PlayersDuetSingerP6: cardinal; - - ColPlayer: array[0..UIni.IMaxPlayerCount-1] of TRGB; + DuetSingerStatics: array[0..UIni.IMaxPlayerCount-1] of cardinal; + DuetSingerTexts: array[0..UIni.IMaxPlayerCount-1] of cardinal; + DuetSingerBaseStatic: TThemeStatic; + DuetSingerBaseText: TThemeText; //CurrentPartyTime: cardinal; //PartyTime: cardinal; @@ -241,6 +226,7 @@ TScreenSong = class(TMenu) procedure SetRouletteScrollRefresh; procedure SetChessboardScrollRefresh; procedure SetListScrollRefresh; + procedure SyncDuetSingerTheme; function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; @@ -310,6 +296,9 @@ TScreenSong = class(TMenu) function getVisibleMedleyArr(MinSource: TMedleySource): TVisArr; procedure ColorDuetNameSingers; + procedure LayoutDuetSingerWidgets(PlayerCount: integer); + procedure SetDuetSingerVisibility(Count: integer); + procedure SetDuetSingerTexts(Count, FirstNameIndex: integer); procedure StopMusicPreview(); procedure StopVideoPreview(); @@ -342,6 +331,17 @@ implementation dglOpenGL, Math; +function GetSongPlayerColor(PlayerIndex: integer): TRGB; +begin + Result := GetPlayerColor(Ini.SingColor[PlayerIndex]); +end; + +procedure TScreenSong.SyncDuetSingerTheme; +begin + DuetSingerBaseStatic := Theme.Song.Static2PlayersDuetSingerP1; + DuetSingerBaseText := Theme.Song.Text2PlayersDuetSingerP1; +end; + const MAX_TIME = 30; MAX_MESSAGE = 3; @@ -1824,25 +1824,13 @@ constructor TScreenSong.Create; InfoMessageBG := AddStatic(Theme.Song.InfoMessageBG); InfoMessageText := AddText(Theme.Song.InfoMessageText); - // Duet Names Singers - Static4PlayersDuetSingerP3 := AddStatic(Theme.Song.Static4PlayersDuetSingerP3); - Static4PlayersDuetSingerP4 := AddStatic(Theme.Song.Static4PlayersDuetSingerP4); - - Static6PlayersDuetSingerP4 := AddStatic(Theme.Song.Static6PlayersDuetSingerP4); - Static6PlayersDuetSingerP5 := AddStatic(Theme.Song.Static6PlayersDuetSingerP5); - Static6PlayersDuetSingerP6 := AddStatic(Theme.Song.Static6PlayersDuetSingerP6); - - Text2PlayersDuetSingerP1 := AddText(Theme.Song.Text2PlayersDuetSingerP1); - Text2PlayersDuetSingerP2 := AddText(Theme.Song.Text2PlayersDuetSingerP2); - Static2PlayersDuetSingerP1 := AddStatic(Theme.Song.Static2PlayersDuetSingerP1); - Static2PlayersDuetSingerP2 := AddStatic(Theme.Song.Static2PlayersDuetSingerP2); - - Text3PlayersDuetSingerP1 := AddText(Theme.Song.Text3PlayersDuetSingerP1); - Text3PlayersDuetSingerP2 := AddText(Theme.Song.Text3PlayersDuetSingerP2); - Text3PlayersDuetSingerP3 := AddText(Theme.Song.Text3PlayersDuetSingerP3); - Static3PlayersDuetSingerP1 := AddStatic(Theme.Song.Static3PlayersDuetSingerP1); - Static3PlayersDuetSingerP2 := AddStatic(Theme.Song.Static3PlayersDuetSingerP2); - Static3PlayersDuetSingerP3 := AddStatic(Theme.Song.Static3PlayersDuetSingerP3); + // Duet singer labels are created from a single base template and laid out at runtime. + SyncDuetSingerTheme; + for I := 0 to High(DuetSingerStatics) do + begin + DuetSingerStatics[I] := AddStatic(DuetSingerBaseStatic); + DuetSingerTexts[I] := AddText(DuetSingerBaseText); + end; // Medley Playlist SetLength(TextMedleyArtist, Theme.Song.TextMedleyMax); @@ -1959,6 +1947,11 @@ constructor TScreenSong.Create; end; procedure TScreenSong.ColorDuetNameSingers(); +var + LocalPlayerCount: integer; + LocalStartIndex: integer; + VisibleCount: integer; + I: integer; procedure setColor(static: integer; color: TRGB); begin Statics[static].Texture.ColR := color.R; @@ -1966,78 +1959,120 @@ procedure TScreenSong.ColorDuetNameSingers(); Statics[static].Texture.ColB := color.B; end; begin - if (PlayersPlay = 1) then + if Screens > 1 then begin - setColor(Static2PlayersDuetSingerP1, ColPlayer[0]); - // this one is different from all the others - setColor(Static2PlayersDuetSingerP2, GetPlayerLightColor(Ini.SingColor[0])); - end; - - if (PlayersPlay = 2) then + LocalPlayerCount := GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct); + LocalStartIndex := GetFirstPlayerIndexForScreen(PlayersPlay, Screens, ScreenAct); + end + else begin - setColor(Static2PlayersDuetSingerP1, ColPlayer[0]); - setColor(Static2PlayersDuetSingerP2, ColPlayer[1]); + LocalPlayerCount := PlayersPlay; + LocalStartIndex := 0; end; - if (PlayersPlay = 3) then + VisibleCount := Max(2, LocalPlayerCount); + LayoutDuetSingerWidgets(VisibleCount); + if LocalPlayerCount = 1 then begin - setColor(Static3PlayersDuetSingerP1, ColPlayer[0]); - setColor(Static3PlayersDuetSingerP2, ColPlayer[1]); - setColor(Static3PlayersDuetSingerP3, ColPlayer[2]); + setColor(DuetSingerStatics[0], GetSongPlayerColor(LocalStartIndex)); + setColor(DuetSingerStatics[1], GetPlayerLightColor(Ini.SingColor[LocalStartIndex])); + end + else + begin + for I := 0 to VisibleCount - 1 do + setColor(DuetSingerStatics[I], GetSongPlayerColor(LocalStartIndex + I)); end; +end; + +procedure TScreenSong.LayoutDuetSingerWidgets(PlayerCount: integer); +var + I: integer; + Grid: TPlayerGrid; + SlotRect: TPlayerSlotRect; + RawTextOffsetX: real; + RawTextOffsetY: real; + TextOffsetX: real; + TextOffsetY: real; + RowScale: real; + BoxWidth: integer; + BoxHeight: integer; + ContainerX: integer; + ContainerY: integer; + ContainerW: integer; + ContainerH: integer; + TotalWidth: integer; +const + TextNudgeX = 3; +begin + if PlayerCount <= 0 then + Exit; + + SyncDuetSingerTheme; + Grid := GetPlayerGrid(PlayerCount); + RowScale := Max(0.72, 1.0 - 0.08 * (Grid.Rows - 1)); + BoxWidth := Max(1, Round(DuetSingerBaseStatic.W * 0.68 * RowScale)); + BoxHeight := Max(1, Round(DuetSingerBaseStatic.H * RowScale)); + RawTextOffsetX := DuetSingerBaseText.X - DuetSingerBaseStatic.X; + RawTextOffsetY := DuetSingerBaseText.Y - DuetSingerBaseStatic.Y; + if (RawTextOffsetX < 0) or (RawTextOffsetX > DuetSingerBaseStatic.W) then + TextOffsetX := BoxWidth * 0.35 + else + TextOffsetX := RawTextOffsetX * (BoxWidth / Max(1.0, DuetSingerBaseStatic.W)); + if (RawTextOffsetY < 0) or (RawTextOffsetY > DuetSingerBaseStatic.H) then + TextOffsetY := BoxHeight * 0.15 + else + TextOffsetY := RawTextOffsetY * (BoxHeight / Max(1.0, DuetSingerBaseStatic.H)); + TotalWidth := Grid.Cols * BoxWidth; + if Grid.Cols > 1 then + Inc(TotalWidth, (Grid.Cols - 1) * 8); - if (PlayersPlay = 4) then + ContainerX := Theme.Song.DuetSingerArea.X; + ContainerY := Theme.Song.DuetSingerArea.Y; + ContainerH := Grid.Rows * BoxHeight; + ContainerW := TotalWidth; + + for I := 0 to High(DuetSingerStatics) do begin - if (Screens = 1) then + if I < PlayerCount then begin - setColor(Static2PlayersDuetSingerP1, ColPlayer[0]); - setColor(Static2PlayersDuetSingerP2, ColPlayer[1]); - setColor(Static4PlayersDuetSingerP3, ColPlayer[2]); - setColor(Static4PlayersDuetSingerP4, ColPlayer[3]); - end - else - begin - if (ScreenAct = 1) then - begin - setColor(Static2PlayersDuetSingerP1, ColPlayer[0]); - setColor(Static2PlayersDuetSingerP2, ColPlayer[1]); - end; - - if (ScreenAct = 2) then - begin - setColor(Static2PlayersDuetSingerP1, ColPlayer[2]); - setColor(Static2PlayersDuetSingerP2, ColPlayer[3]); - end; + SlotRect := GetPlayerSlotRect(I, PlayerCount, ContainerX, ContainerY, ContainerW, ContainerH); + Statics[DuetSingerStatics[I]].Texture.X := SlotRect.X; + Statics[DuetSingerStatics[I]].Texture.Y := SlotRect.Y; + Statics[DuetSingerStatics[I]].Texture.W := BoxWidth; + Statics[DuetSingerStatics[I]].Texture.H := BoxHeight; + Statics[DuetSingerStatics[I]].Texture.Z := Max(DuetSingerBaseStatic.Z, 0.995); + + Text[DuetSingerTexts[I]].X := Statics[DuetSingerStatics[I]].Texture.X + TextOffsetX + TextNudgeX; + Text[DuetSingerTexts[I]].Y := Statics[DuetSingerStatics[I]].Texture.Y + TextOffsetY; + Text[DuetSingerTexts[I]].W := BoxWidth; + Text[DuetSingerTexts[I]].H := Max(1, Round(DuetSingerBaseText.H * RowScale)); + Text[DuetSingerTexts[I]].Size := Max(8, Round(DuetSingerBaseText.Size * RowScale)); + Text[DuetSingerTexts[I]].Z := Max(DuetSingerBaseText.Z, 0.996); end; end; +end; - if (PlayersPlay = 6) then +procedure TScreenSong.SetDuetSingerVisibility(Count: integer); +var + I: integer; +begin + for I := 0 to High(DuetSingerStatics) do begin - if (Screens = 1) then - begin - setColor(Static3PlayersDuetSingerP1, ColPlayer[0]); - setColor(Static3PlayersDuetSingerP2, ColPlayer[1]); - setColor(Static3PlayersDuetSingerP3, ColPlayer[2]); - setColor(Static6PlayersDuetSingerP4, ColPlayer[3]); - setColor(Static6PlayersDuetSingerP5, ColPlayer[4]); - setColor(Static6PlayersDuetSingerP6, ColPlayer[5]); - end - else - begin - if (ScreenAct = 1) then - begin - setColor(Static3PlayersDuetSingerP1, ColPlayer[0]); - setColor(Static3PlayersDuetSingerP2, ColPlayer[1]); - setColor(Static3PlayersDuetSingerP3, ColPlayer[2]); - end; + Statics[DuetSingerStatics[I]].Visible := I < Count; + Text[DuetSingerTexts[I]].Visible := I < Count; + end; +end; - if (ScreenAct = 2) then - begin - setColor(Static3PlayersDuetSingerP1, ColPlayer[3]); - setColor(Static3PlayersDuetSingerP2, ColPlayer[4]); - setColor(Static3PlayersDuetSingerP3, ColPlayer[5]); - end; - end; +procedure TScreenSong.SetDuetSingerTexts(Count, FirstNameIndex: integer); +var + I: integer; +begin + for I := 0 to High(DuetSingerTexts) do + begin + if I < Count then + Text[DuetSingerTexts[I]].Text := CatSongs.Song[Interaction].DuetNames[(FirstNameIndex + (I mod 2)) mod 2] + else + Text[DuetSingerTexts[I]].Text := ''; end; end; @@ -2156,29 +2191,10 @@ procedure TScreenSong.SetScrollRefresh; procedure TScreenSong.SetScroll; var VS, B, SongsInCat: integer; - procedure HideDuetElements; - begin - Text[Text2PlayersDuetSingerP1].Visible := false; - Text[Text2PlayersDuetSingerP2].Visible := false; - - Statics[Static2PlayersDuetSingerP1].Visible := false; - Statics[Static2PlayersDuetSingerP2].Visible := false; - - Text[Text3PlayersDuetSingerP1].Visible := false; - Text[Text3PlayersDuetSingerP2].Visible := false; - Text[Text3PlayersDuetSingerP3].Visible := false; - - Statics[Static3PlayersDuetSingerP1].Visible := false; - Statics[Static3PlayersDuetSingerP2].Visible := false; - Statics[Static3PlayersDuetSingerP3].Visible := false; - - Statics[Static4PlayersDuetSingerP3].Visible := false; - Statics[Static4PlayersDuetSingerP4].Visible := false; - - Statics[Static6PlayersDuetSingerP4].Visible := false; - Statics[Static6PlayersDuetSingerP5].Visible := false; - Statics[Static6PlayersDuetSingerP6].Visible := false; - end; + LocalPlayerCount: integer; + LocalStartIndex: integer; + BaseNameIndex: integer; + VisibleCount: integer; begin VS := CatSongs.VisibleSongs; if VS > 0 then @@ -2218,108 +2234,40 @@ procedure TScreenSong.SetScroll; Text[TextYear].Text := ''; end; - HideDuetElements; + SetDuetSingerVisibility(0); // Duet Singers if (CatSongs.Song[Interaction].isDuet) then begin - if (PlayersPlay = 3) or (PlayersPlay = 6) then + if Screens > 1 then begin - Text[Text3PlayersDuetSingerP1].Visible := true; - Text[Text3PlayersDuetSingerP2].Visible := true; - Text[Text3PlayersDuetSingerP3].Visible := true; - - Statics[Static3PlayersDuetSingerP1].Visible := true; - Statics[Static3PlayersDuetSingerP2].Visible := true; - Statics[Static3PlayersDuetSingerP3].Visible := true; - - if (Screens = 1) and (PlayersPlay = 6) then - begin - Statics[Static6PlayersDuetSingerP4].Visible := true; - Statics[Static6PlayersDuetSingerP5].Visible := true; - Statics[Static6PlayersDuetSingerP6].Visible := true; - end; + LocalPlayerCount := GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct); + LocalStartIndex := GetFirstPlayerIndexForScreen(PlayersPlay, Screens, ScreenAct); end else begin - Text[Text2PlayersDuetSingerP1].Visible := true; - Text[Text2PlayersDuetSingerP2].Visible := true; - - Statics[Static2PlayersDuetSingerP1].Visible := true; - Statics[Static2PlayersDuetSingerP2].Visible := true; - - if (Screens = 1) and (PlayersPlay = 4) then - begin - Statics[Static4PlayersDuetSingerP3].Visible := true; - Statics[Static4PlayersDuetSingerP4].Visible := true; - end; + LocalPlayerCount := PlayersPlay; + LocalStartIndex := 0; end; + VisibleCount := Max(2, LocalPlayerCount); + LayoutDuetSingerWidgets(VisibleCount); + SetDuetSingerVisibility(VisibleCount); + // Set duet texts - if (DuetChange) then + if Screens > 1 then begin - if (PlayersPlay = 3) or (PlayersPlay = 6) then - begin - if (PlayersPlay = 3) then - begin - Text[Text3PlayersDuetSingerP1].Text := CatSongs.Song[Interaction].DuetNames[1]; - Text[Text3PlayersDuetSingerP2].Text := CatSongs.Song[Interaction].DuetNames[0]; - Text[Text3PlayersDuetSingerP3].Text := CatSongs.Song[Interaction].DuetNames[1]; - end - else - begin - if (ScreenAct = 1) then - begin - Text[Text3PlayersDuetSingerP1].Text := CatSongs.Song[Interaction].DuetNames[1]; - Text[Text3PlayersDuetSingerP2].Text := CatSongs.Song[Interaction].DuetNames[0]; - Text[Text3PlayersDuetSingerP3].Text := CatSongs.Song[Interaction].DuetNames[1]; - end - else - begin - Text[Text3PlayersDuetSingerP1].Text := CatSongs.Song[Interaction].DuetNames[0]; - Text[Text3PlayersDuetSingerP2].Text := CatSongs.Song[Interaction].DuetNames[1]; - Text[Text3PlayersDuetSingerP3].Text := CatSongs.Song[Interaction].DuetNames[0]; - end; - end; - end + if LocalPlayerCount <= 2 then + BaseNameIndex := Ord(not DuetChange) else - begin - Text[Text2PlayersDuetSingerP1].Text := CatSongs.Song[Interaction].DuetNames[1]; - Text[Text2PlayersDuetSingerP2].Text := CatSongs.Song[Interaction].DuetNames[0]; - end; + BaseNameIndex := (LocalStartIndex + Ord(DuetChange)) mod 2; end else begin - if (PlayersPlay = 3) or (PlayersPlay = 6) then - begin - if (PlayersPlay = 3) then - begin - Text[Text3PlayersDuetSingerP1].Text := CatSongs.Song[Interaction].DuetNames[0]; - Text[Text3PlayersDuetSingerP2].Text := CatSongs.Song[Interaction].DuetNames[1]; - Text[Text3PlayersDuetSingerP3].Text := CatSongs.Song[Interaction].DuetNames[0]; - end - else - begin - if (ScreenAct = 1) then - begin - Text[Text3PlayersDuetSingerP1].Text := CatSongs.Song[Interaction].DuetNames[0]; - Text[Text3PlayersDuetSingerP2].Text := CatSongs.Song[Interaction].DuetNames[1]; - Text[Text3PlayersDuetSingerP3].Text := CatSongs.Song[Interaction].DuetNames[0]; - end - else - begin - Text[Text3PlayersDuetSingerP1].Text := CatSongs.Song[Interaction].DuetNames[1]; - Text[Text3PlayersDuetSingerP2].Text := CatSongs.Song[Interaction].DuetNames[0]; - Text[Text3PlayersDuetSingerP3].Text := CatSongs.Song[Interaction].DuetNames[1]; - end; - end; - end - else - begin - Text[Text2PlayersDuetSingerP1].Text := CatSongs.Song[Interaction].DuetNames[0]; - Text[Text2PlayersDuetSingerP2].Text := CatSongs.Song[Interaction].DuetNames[1]; - end; + BaseNameIndex := Ord(DuetChange); end; + + SetDuetSingerTexts(VisibleCount, BaseNameIndex); end; //Set Song Score SongScore; @@ -2354,7 +2302,7 @@ procedure TScreenSong.SetScroll; Statics[VideoIcon].Visible := false; - HideDuetElements; + SetDuetSingerVisibility(0); for B := 0 to High(Button) do Button[B].Visible := false; @@ -2844,6 +2792,7 @@ procedure TScreenSong.OnShow; I: integer; begin inherited; + SyncDuetSingerTheme; CloseMessage(); @@ -2920,14 +2869,6 @@ procedure TScreenSong.OnShow; Statics[RapToFreestyleIcon].Visible := false; end; - // for duet names - ScreenSong.ColPlayer[0] := GetPlayerColor(Ini.SingColor[0]); - ScreenSong.ColPlayer[1] := GetPlayerColor(Ini.SingColor[1]); - ScreenSong.ColPlayer[2] := GetPlayerColor(Ini.SingColor[2]); - ScreenSong.ColPlayer[3] := GetPlayerColor(Ini.SingColor[3]); - ScreenSong.ColPlayer[4] := GetPlayerColor(Ini.SingColor[4]); - ScreenSong.ColPlayer[5] := GetPlayerColor(Ini.SingColor[5]); - {** * Pause background music *} @@ -2948,8 +2889,7 @@ procedure TScreenSong.OnShow; if Mode = smMedley then Mode := smNormal; - if Ini.Players <= 3 then PlayersPlay := Ini.Players + 1; - if Ini.Players = 4 then PlayersPlay := 6; + PlayersPlay := UIni.IPlayersVals[Ini.Players]; //Cat Mod etc if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow = -1) then @@ -3019,6 +2959,7 @@ procedure TScreenSong.OnShowFinish; begin DuetChange := false; RapToFreestyle := false; + SyncDuetSingerTheme; isScrolling := true; CoverTime := 10; @@ -3036,6 +2977,7 @@ procedure TScreenSong.OnShowFinish; SetScrollRefresh; FixSelected; + SetScroll; //if (Mode = smPartyTournament) then // PartyTime := SDL_GetTicks(); @@ -3323,6 +3265,17 @@ function TScreenSong.Draw: boolean; for I := 0 to High(Text) do Text[I].Draw; + // Keep duet assignment widgets in the foreground to avoid mode-specific overlap. + if CatSongs.Song[Interaction].isDuet then + begin + for I := 0 to High(DuetSingerStatics) do + if Statics[DuetSingerStatics[I]].Visible then + Statics[DuetSingerStatics[I]].Draw; + for I := 0 to High(DuetSingerTexts) do + if Text[DuetSingerTexts[I]].Visible then + Text[DuetSingerTexts[I]].Draw; + end; + Equalizer.Draw; DrawExtensions; @@ -4396,7 +4349,7 @@ procedure TScreenSong.SongScore; end; begin - if (CatSongs.Song[Interaction].isDuet) or (RapToFreestyle) or ((Mode <> smNormal) or (Ini.ShowScores = 0) or (CatSongs.Song[Interaction].Edition = '') or ((Ini.ShowScores = 1) and ((Text[TextMaxScore2].Text = '0') and (Text[TextMaxScoreLocal].Text = '0')))) then + if RapToFreestyle or ((Mode <> smNormal) or (Ini.ShowScores = 0) or (CatSongs.Song[Interaction].Edition = '') or ((Ini.ShowScores = 1) and ((Text[TextMaxScore2].Text = '0') and (Text[TextMaxScoreLocal].Text = '0')))) then begin hide([ TextScore, TextMaxScore, TextMediaScore, From e31a8f1f0922e9fd9d0e8ba2a22734aff6e83473 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:31:32 +0200 Subject: [PATCH 07/11] Use generic layout for score screen --- src/screens/UScreenScore.pas | 889 ++++++++++++++--------------------- 1 file changed, 365 insertions(+), 524 deletions(-) diff --git a/src/screens/UScreenScore.pas b/src/screens/UScreenScore.pas index d2dbc27ac..7ae8ef83a 100644 --- a/src/screens/UScreenScore.pas +++ b/src/screens/UScreenScore.pas @@ -58,7 +58,7 @@ interface type TScoreBarType = (sbtScore, sbtLine, sbtGolden); - TPlayerScoreScreenTexture = record // holds all colorized textures for up to 6 players + TPlayerScoreScreenTexture = record // holds all colorized textures for one player //Bar textures Score_NoteBarLevel_Dark: TTexture; // Note Score_NoteBarRound_Dark: TTexture; // that's the round thing on top @@ -85,9 +85,8 @@ TPlayerScoreRatingPics = record // a fine array of the rating pi { hold maps of players to the different positions } TPlayerPositionMap = record - Position: byte; // 1..6: Position of Player; 0: no position (e.g. too little screens) + Position: byte; // local slot index on the assigned screen; 0 if unused Screen: byte; // 0 - Screen 1; 1 - Screen 2 - BothScreens: boolean; // true if player is drawn on both screens end; APlayerPositionMap = array of TPlayerPositionMap; @@ -131,30 +130,22 @@ TScreenScore = class(TMenu) TextTotalScore: array[1..UIni.IMaxPlayerCount] of integer; PlayerStatic: array[1..UIni.IMaxPlayerCount] of array of integer; - AvatarStatic: array[1..UIni.IMaxPlayerCount] of integer; AvatarStaticRef: array[1..UIni.IMaxPlayerCount] of Integer; { texture pairs for swapping when screens = 2 - first array level: index of player ( actually this is a position - 1 - Player 1 if PlayersPlay = 1 <- we don't need swapping here - 2..3 - Player 1 and 2 or 3 and 4 if PlayersPlay = 2 or 4 - 4..6 - Player 1 - 3 or 4 - 6 if PlayersPlay = 3 or 6 ) - second array level: different playerstatics for positions - third array level: texture for screen 1 or 2 } - PlayerStaticTextures: array[1..UIni.IMaxPlayerCount] of array of array [1..2] of TPlayerStaticTexture; + first array level: slot index on a screen + second array level: static index within that slot + third array level: precolored texture for the target player } + PlayerStaticTextures: array[1..UIni.IMaxPlayerCount] of array of array [1..UIni.IMaxPlayerCount] of TPlayerStaticTexture; PlayerTexts: array[1..UIni.IMaxPlayerCount] of array of integer; StaticBoxLightest: array[1..UIni.IMaxPlayerCount] of integer; StaticBoxLight: array[1..UIni.IMaxPlayerCount] of integer; StaticBoxDark: array[1..UIni.IMaxPlayerCount] of integer; { texture pairs for swapping when screens = 2 - for boxes - first array level: index of player ( actually this is a position - 1 - Player 1 if PlayersPlay = 1 <- we don't need swapping here - 2..3 - Player 1 and 2 or 3 and 4 if PlayersPlay = 2 or 4 - 4..6 - Player 1 - 3 or 4 - 6 if PlayersPlay = 3 or 6 ) - second array level: different boxes for positions (0: lightest; 1: light; 2: dark) - third array level: texture for screen 1 or 2 } - PlayerBoxTextures: array[1..UIni.IMaxPlayerCount] of array[0..2] of array [1..2] of TPlayerStaticTexture; + first array level: slot index on a screen + second array level: box variant (0: lightest; 1: light; 2: dark) + third array level: precolored texture for the target player } + PlayerBoxTextures: array[1..UIni.IMaxPlayerCount] of array[0..2] of array [1..UIni.IMaxPlayerCount] of TPlayerStaticTexture; StaticBackLevel: array[1..UIni.IMaxPlayerCount] of integer; StaticBackLevelRound: array[1..UIni.IMaxPlayerCount] of integer; @@ -167,7 +158,7 @@ TScreenScore = class(TMenu) TextPhrase_ActualValue: array[1..UIni.IMaxPlayerCount] of integer; TextGolden_ActualValue: array[1..UIni.IMaxPlayerCount] of integer; - ButtonSend: array[1..UIni.IMaxPlayerCount] of integer; + ButtonSend: array[1..3] of integer; CurrentRound: integer; StaticNavigate: integer; TextNavigate: integer; @@ -226,6 +217,7 @@ implementation ULog, UMenuStatic, UNote, + UPlayerLayout, UPathUtils, UScreenPopup, UScreenSong, @@ -234,6 +226,205 @@ implementation UTime, UUnicodeUtils; +function GetScoreSlotIndex(PlayerIndex, PlayerCount, ScreenCount: integer): integer; +begin + Result := GetPlayerIndexOnScreen(PlayerIndex, PlayerCount, ScreenCount) + 1; +end; + +function ReplaceBasePlayerColor(const Color: string; TargetPlayer: integer): string; +var + SourcePrefix: string; +begin + Result := Color; + SourcePrefix := 'P1'; + if Copy(Color, 1, Length(SourcePrefix)) = SourcePrefix then + Result := 'P' + IntToStr(TargetPlayer) + Copy(Color, Length(SourcePrefix) + 1, MaxInt); +end; + +function GetScorePlayerThemeColor(TargetPlayer: integer; const ThemeColor: string; out Col: TRGB): boolean; +var + BaseColor: integer; +begin + Result := false; + if (TargetPlayer < 1) or (TargetPlayer > Length(Ini.PlayerColor)) then + Exit; + + if Pos('P1', ThemeColor) <> 1 then + Exit; + + BaseColor := Ini.PlayerColor[TargetPlayer - 1]; + if Pos('Lightest', ThemeColor) = 3 then + Col := ColorSqrt(GetPlayerLightColor(BaseColor)) + else if Pos('Light', ThemeColor) = 3 then + Col := GetPlayerLightColor(BaseColor) + else + Col := GetPlayerColor(BaseColor); + + Result := true; +end; + +function ResolvePlayerThemeColor(const ThemeColor: string; TargetPlayer: integer; out Col: TRGB): boolean; +var + ResolvedColor: string; + R: real; + G: real; + B: real; +begin + Result := false; + if ThemeColor = '' then + Exit; + + if GetScorePlayerThemeColor(TargetPlayer, ThemeColor, Col) then + Exit(true); + + ResolvedColor := ReplaceBasePlayerColor(ThemeColor, TargetPlayer); + if ResolvedColor <> ThemeColor then + begin + LoadColor(R, G, B, ResolvedColor); + Col.R := R; + Col.G := G; + Col.B := B; + Result := true; + end; +end; + +procedure ApplyThemeStaticForPlayer(const ScreenScore: TScreenScore; StaticIndex: integer; + const ThemeStatic: TThemeStatic; TargetPlayer: integer); +var + Col: TRGB; + CurrentTexture: TTexture; +begin + CurrentTexture := ScreenScore.Statics[StaticIndex].Texture; + if ResolvePlayerThemeColor(ThemeStatic.Color, TargetPlayer, Col) then + begin + if ThemeStatic.Typ = Texture_Type_Colorized then + begin + ScreenScore.Statics[StaticIndex].Texture := + Texture.GetTexture(Skin.GetTextureFileName(ThemeStatic.Tex), ThemeStatic.Typ, + RGBFloatToInt(Col.R, Col.G, Col.B)); + ScreenScore.Statics[StaticIndex].Texture.X := CurrentTexture.X; + ScreenScore.Statics[StaticIndex].Texture.Y := CurrentTexture.Y; + ScreenScore.Statics[StaticIndex].Texture.W := CurrentTexture.W; + ScreenScore.Statics[StaticIndex].Texture.H := CurrentTexture.H; + ScreenScore.Statics[StaticIndex].Texture.Z := CurrentTexture.Z; + ScreenScore.Statics[StaticIndex].Texture.Alpha := CurrentTexture.Alpha; + end + else + begin + ScreenScore.Statics[StaticIndex].Texture.ColR := Col.R; + ScreenScore.Statics[StaticIndex].Texture.ColG := Col.G; + ScreenScore.Statics[StaticIndex].Texture.ColB := Col.B; + end; + end; +end; + +function GetScoreButtonLayout(PlayerCount, ScreenCount: integer): integer; +begin + Result := EnsureRange(GetScorePlayerGrid(PlayerCount, + Theme.Score.PlayerLayoutArea.W, Theme.Score.PlayerLayoutArea.H, + Theme.Score.PlayerLayoutExtraColsBias).Cols, 1, 3); +end; + +procedure SetScoreSlotScoreAlpha(const ScreenScore: TScreenScore; SlotIndex: integer; Alpha: real); +begin + ScreenScore.Text[ScreenScore.TextScore[SlotIndex]].Alpha := Alpha; + ScreenScore.Text[ScreenScore.TextNotes[SlotIndex]].Alpha := Alpha; + ScreenScore.Text[ScreenScore.TextNotesScore[SlotIndex]].Alpha := Alpha; + ScreenScore.Text[ScreenScore.TextLineBonus[SlotIndex]].Alpha := Alpha; + ScreenScore.Text[ScreenScore.TextLineBonusScore[SlotIndex]].Alpha := Alpha; + ScreenScore.Text[ScreenScore.TextGoldenNotes[SlotIndex]].Alpha := Alpha; + ScreenScore.Text[ScreenScore.TextGoldenNotesScore[SlotIndex]].Alpha := Alpha; + ScreenScore.Text[ScreenScore.TextTotal[SlotIndex]].Alpha := Alpha; + ScreenScore.Text[ScreenScore.TextTotalScore[SlotIndex]].Alpha := Alpha; + ScreenScore.Statics[ScreenScore.StaticBoxLightest[SlotIndex]].Texture.Alpha := Alpha; + ScreenScore.Statics[ScreenScore.StaticBoxLight[SlotIndex]].Texture.Alpha := Alpha; + ScreenScore.Statics[ScreenScore.StaticBoxDark[SlotIndex]].Texture.Alpha := Alpha; +end; + +procedure SetScoreSlotVisible(const ScreenScore: TScreenScore; SlotIndex: integer; Visible: boolean); +var + I: integer; +begin + ScreenScore.Text[ScreenScore.TextName[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextScore[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextNotes[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextNotesScore[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextLineBonus[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextLineBonusScore[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextGoldenNotes[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextGoldenNotesScore[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextTotal[SlotIndex]].Visible := Visible; + ScreenScore.Text[ScreenScore.TextTotalScore[SlotIndex]].Visible := Visible; + + for I := 0 to High(ScreenScore.PlayerStatic[SlotIndex]) do + ScreenScore.Statics[ScreenScore.PlayerStatic[SlotIndex, I]].Visible := Visible; + + for I := 0 to High(ScreenScore.PlayerTexts[SlotIndex]) do + ScreenScore.Text[ScreenScore.PlayerTexts[SlotIndex, I]].Visible := Visible; + + ScreenScore.Statics[ScreenScore.StaticBoxLightest[SlotIndex]].Visible := Visible; + ScreenScore.Statics[ScreenScore.StaticBoxLight[SlotIndex]].Visible := Visible; + ScreenScore.Statics[ScreenScore.StaticBoxDark[SlotIndex]].Visible := Visible; +end; + +procedure ApplyScoreSlotTextColors(const ScreenScore: TScreenScore; SlotIndex, TargetPlayer: integer); +var + I: integer; + procedure ApplyTextColor(TextIndex: integer; const ThemeColor: string); + var + Col: TRGB; + begin + if ResolvePlayerThemeColor(ThemeColor, TargetPlayer, Col) then + begin + ScreenScore.Text[TextIndex].ColR := Col.R; + ScreenScore.Text[TextIndex].ColG := Col.G; + ScreenScore.Text[TextIndex].ColB := Col.B; + end; + end; +begin + for I := 0 to High(ScreenScore.PlayerTexts[SlotIndex]) do + ApplyTextColor(ScreenScore.PlayerTexts[SlotIndex, I], Theme.Score.PlayerTexts[SlotIndex, I].Color); + + ApplyTextColor(ScreenScore.TextName[SlotIndex], Theme.Score.TextName[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextScore[SlotIndex], Theme.Score.TextScore[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextNotes[SlotIndex], Theme.Score.TextNotes[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextNotesScore[SlotIndex], Theme.Score.TextNotesScore[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextLineBonus[SlotIndex], Theme.Score.TextLineBonus[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextLineBonusScore[SlotIndex], Theme.Score.TextLineBonusScore[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextGoldenNotes[SlotIndex], Theme.Score.TextGoldenNotes[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextGoldenNotesScore[SlotIndex], Theme.Score.TextGoldenNotesScore[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextTotal[SlotIndex], Theme.Score.TextTotal[SlotIndex].Color); + ApplyTextColor(ScreenScore.TextTotalScore[SlotIndex], Theme.Score.TextTotalScore[SlotIndex].Color); +end; + +procedure ApplyScoreSlotPlayerVisuals(const ScreenScore: TScreenScore; SlotIndex, TargetPlayer: integer); +var + I: integer; +begin + ApplyScoreSlotTextColors(ScreenScore, SlotIndex, TargetPlayer); + + for I := 0 to High(ScreenScore.PlayerStatic[SlotIndex]) do + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.PlayerStatic[SlotIndex, I], + Theme.Score.PlayerStatic[SlotIndex, I], TargetPlayer); + + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.StaticBoxLightest[SlotIndex], + Theme.Score.StaticBoxLightest[SlotIndex], TargetPlayer); + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.StaticBoxLight[SlotIndex], + Theme.Score.StaticBoxLight[SlotIndex], TargetPlayer); + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.StaticBoxDark[SlotIndex], + Theme.Score.StaticBoxDark[SlotIndex], TargetPlayer); + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.StaticBackLevel[SlotIndex], + Theme.Score.StaticBackLevel[SlotIndex], TargetPlayer); + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.StaticBackLevelRound[SlotIndex], + Theme.Score.StaticBackLevelRound[SlotIndex], TargetPlayer); + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.StaticLevel[SlotIndex], + Theme.Score.StaticLevel[SlotIndex], TargetPlayer); + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.StaticLevelRound[SlotIndex], + Theme.Score.StaticLevelRound[SlotIndex], TargetPlayer); + ApplyThemeStaticForPlayer(ScreenScore, ScreenScore.AvatarStaticRef[TargetPlayer], + Theme.Score.AvatarStatic[SlotIndex], TargetPlayer); +end; + { ***************************** ** 100: NOT A VALID DLL :p @@ -480,14 +671,7 @@ function TScreenScore.ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: i begin Result := True; - //TODO: adapt for players 7 to 12 - case PlayersPlay of - 1 : button_s := ButtonSend[1]; - 2, 4: button_s := ButtonSend[2]; - 3, 6: button_s := ButtonSend[3]; - else - button_s := ButtonSend[3]; - end; + button_s := ButtonSend[GetScoreButtonLayout(PlayersPlay, Screens)]; // transfer mousecords to the 800x600 raster we use to draw X := Round((X / (ScreenW / Screens)) * RenderW); @@ -548,96 +732,42 @@ procedure TScreenScore.RefreshTexts; ResetScores; end; -//TODO: adapt for players 7 to 12 procedure TScreenScore.LoadSwapTextures; - var - P, I: integer; - PlayerNum, PlayerNum2: integer; - Color: string; - R, G, B: real; - StaticNum: integer; - ThemeStatic: TThemeStatic; +var + P, I, TargetPlayer: integer; + Col: TRGB; + StaticNum: integer; + ThemeStatic: TThemeStatic; begin - { we only need to load swapping textures if in dualscreen mode } if Screens = 2 then begin - { load swapping textures for custom statics } for P := low(PlayerStatic) to High(PlayerStatic) do begin SetLength(PlayerStaticTextures[P], Length(PlayerStatic[P])); - { get the players that actually are on this position } - case P of - 1: begin - PlayerNum := 1; - PlayerNum2 := 1; - end; - - 2, 3: begin - PlayerNum := P - 1; - PlayerNum2 := PlayerNum + 2; - end; - - 4..6: begin - PlayerNum := P - 3; - PlayerNum2 := PlayerNum + 3; - end; - end; - for I := 0 to High(PlayerStatic[P]) do begin - // copy current statics texture to texture for screen 1 - PlayerStaticTextures[P, I, 1].Tex := Statics[PlayerStatic[P, I]].Texture; - - // fallback to first screen texture for 2nd screen - PlayerStaticTextures[P, I, 2].Tex := PlayerStaticTextures[P, I, 1].Tex; - - { texture for second screen } - { we only change color for statics with playercolor - and with Texture type colorized - also we don't need to swap for one player position } - if (P <> 1) and - (Theme.Score.PlayerStatic[P, I].Typ = Texture_Type_Colorized) and - (Length(Theme.Score.PlayerStatic[P, I].Color) >= 2) and - (copy(Theme.Score.PlayerStatic[P, I].Color, 1, 2) = 'P' + IntToStr(PlayerNum)) then + for TargetPlayer := 1 to PlayersPlay do begin - // get the color - Color := Theme.Score.PlayerStatic[P, I].Color; - Color[2] := IntToStr(PlayerNum2)[1]; - LoadColor(R, G, B, Color); - - with Theme.Score.PlayerStatic[P, I] do - PlayerStaticTextures[P, I, 2].Tex := Texture.GetTexture(Skin.GetTextureFileName(Tex), Typ, RGBFloatToInt(R, G, B)); - - PlayerStaticTextures[P, I, 2].Tex.X := Statics[PlayerStatic[P, I]].Texture.X; - PlayerStaticTextures[P, I, 2].Tex.Y := Statics[PlayerStatic[P, I]].Texture.Y; - PlayerStaticTextures[P, I, 2].Tex.W := Statics[PlayerStatic[P, I]].Texture.W; - PlayerStaticTextures[P, I, 2].Tex.H := Statics[PlayerStatic[P, I]].Texture.H; + PlayerStaticTextures[P, I, TargetPlayer].Tex := Statics[PlayerStatic[P, I]].Texture; + + if (Theme.Score.PlayerStatic[P, I].Typ = Texture_Type_Colorized) and + ResolvePlayerThemeColor(Theme.Score.PlayerStatic[P, I].Color, TargetPlayer, Col) then + begin + with Theme.Score.PlayerStatic[P, I] do + PlayerStaticTextures[P, I, TargetPlayer].Tex := Texture.GetTexture(Skin.GetTextureFileName(Tex), Typ, RGBFloatToInt(Col.R, Col.G, Col.B)); + + PlayerStaticTextures[P, I, TargetPlayer].Tex.X := Statics[PlayerStatic[P, I]].Texture.X; + PlayerStaticTextures[P, I, TargetPlayer].Tex.Y := Statics[PlayerStatic[P, I]].Texture.Y; + PlayerStaticTextures[P, I, TargetPlayer].Tex.W := Statics[PlayerStatic[P, I]].Texture.W; + PlayerStaticTextures[P, I, TargetPlayer].Tex.H := Statics[PlayerStatic[P, I]].Texture.H; + end; end; end; end; - { load swap textures for boxes } for P := low(PlayerBoxTextures) to High(PlayerBoxTextures) do begin - { get the players that actually are on this position } - case P of - 1: begin - PlayerNum := 1; - PlayerNum2 := 1; - end; - - 2, 3: begin - PlayerNum := P - 1; - PlayerNum2 := PlayerNum + 2; - end; - - 4..6: begin - PlayerNum := P - 3; - PlayerNum2 := PlayerNum + 3; - end; - end; - for I := 0 to High(PlayerBoxTextures[P]) do begin case I of @@ -654,46 +784,32 @@ procedure TScreenScore.LoadSwapTextures; ThemeStatic := Theme.Score.StaticBoxDark[P]; end; end; - // copy current statics texture to texture for screen 1 - PlayerBoxTextures[P, I, 1].Tex := Statics[StaticNum].Texture; - - // fallback to first screen texture for 2nd screen - PlayerBoxTextures[P, I, 2].Tex := PlayerBoxTextures[P, I, 1].Tex; - - { texture for second screen } - { we only change color for statics with playercolor - and with Texture type colorized - also we don't need to swap for one player position } - if (P <> 1) and - (ThemeStatic.Typ = Texture_Type_Colorized) and - (Length(ThemeStatic.Color) >= 2) and - (copy(ThemeStatic.Color, 1, 2) = 'P' + IntToStr(PlayerNum)) then + for TargetPlayer := 1 to PlayersPlay do begin - // get the color - Color := ThemeStatic.Color; - Color[2] := IntToStr(PlayerNum2)[1]; - LoadColor(R, G, B, Color); - - with ThemeStatic do - PlayerBoxTextures[P, I, 2].Tex := Texture.GetTexture(Skin.GetTextureFileName(Tex), Typ, RGBFloatToInt(R, G, B)); - - PlayerBoxTextures[P, I, 2].Tex.X := Statics[StaticNum].Texture.X; - PlayerBoxTextures[P, I, 2].Tex.Y := Statics[StaticNum].Texture.Y; - PlayerBoxTextures[P, I, 2].Tex.W := Statics[StaticNum].Texture.W; - PlayerBoxTextures[P, I, 2].Tex.H := Statics[StaticNum].Texture.H; + PlayerBoxTextures[P, I, TargetPlayer].Tex := Statics[StaticNum].Texture; + + if (ThemeStatic.Typ = Texture_Type_Colorized) and + ResolvePlayerThemeColor(ThemeStatic.Color, TargetPlayer, Col) then + begin + with ThemeStatic do + PlayerBoxTextures[P, I, TargetPlayer].Tex := Texture.GetTexture(Skin.GetTextureFileName(Tex), Typ, RGBFloatToInt(Col.R, Col.G, Col.B)); + + PlayerBoxTextures[P, I, TargetPlayer].Tex.X := Statics[StaticNum].Texture.X; + PlayerBoxTextures[P, I, TargetPlayer].Tex.Y := Statics[StaticNum].Texture.Y; + PlayerBoxTextures[P, I, TargetPlayer].Tex.W := Statics[StaticNum].Texture.W; + PlayerBoxTextures[P, I, TargetPlayer].Tex.H := Statics[StaticNum].Texture.H; + end; end; end; end; end; end; -//TODO: adapt for players 7 to 12 procedure TScreenScore.SwapToScreen(Screen: integer); var - P, I, J, Max: integer; - Col: TRGB; + P, I, J: integer; - function FindPlayerIndexForThemeSlot(const ThemeSlot, TargetScreen: integer): integer; + function FindPlayerIndexForSlot(const SlotIndex, TargetScreen: integer): integer; var PlayerIdx: integer; begin @@ -704,151 +820,42 @@ procedure TScreenScore.SwapToScreen(Screen: integer); for PlayerIdx := Low(PlayerPositionMap) to High(PlayerPositionMap) do begin - if (PlayerPositionMap[PlayerIdx].Position = ThemeSlot) and - ((TargetScreen = PlayerPositionMap[PlayerIdx].Screen) or PlayerPositionMap[PlayerIdx].BothScreens) then + if (PlayerPositionMap[PlayerIdx].Position = SlotIndex) and + (TargetScreen = PlayerPositionMap[PlayerIdx].Screen) then begin Result := PlayerIdx; Exit; end; end; end; -begin - case PlayersPlay of - 1: Max := 1; - 2, 4: Max := 2; - 3, 6: Max := 3; - 8: Max := 4; - 12: Max := 6; - else - Max := 0; //this should never happen - end; - - { if screens = 2 and playerplay <= 3 the 2nd screen shows the - textures of screen 1 } - if (PlayersPlay <= 3) and (Screen = 2) then - Screen := 1; - - { set correct box textures } +begin if (Screens = 2) then begin - - for I:= 0 to Max - 1 do + for P := 1 to UIni.IMaxPlayerCount do begin + J := FindPlayerIndexForSlot(P, Screen); + SetScoreSlotVisible(Self, P, J <> -1); - J := FindPlayerIndexForThemeSlot(I + 1 + Max, Screen); - if (J <> -1) and (J <= High(Ini.PlayerColor)) then - Col := GetPlayerColor(Ini.PlayerColor[J]) - else if (Screen = 2) and (I + Max <= High(Ini.PlayerColor)) then - Col := GetPlayerColor(Ini.PlayerColor[I + Max]) - else if (I <= High(Ini.PlayerColor)) then - Col := GetPlayerColor(Ini.PlayerColor[I]) - else + if J = -1 then Continue; - if (copy(Theme.Score.TextName[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextName[I + 1 + Max]].ColR := Col.R; - Text[TextName[I + 1 + Max]].ColG := Col.G; - Text[TextName[I + 1 + Max]].ColB := Col.B; - end; - - if (copy(Theme.Score.TextScore[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextScore[I + 1 + Max]].ColR := Col.R; - Text[TextScore[I + 1 + Max]].ColG := Col.G; - Text[TextScore[I + 1 + Max]].ColB := Col.B; - end; - - if (copy(Theme.Score.TextNotes[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextNotes[I + 1 + Max]].ColR := Col.R; - Text[TextNotes[I + 1 + Max]].ColG := Col.G; - Text[TextNotes[I + 1 + Max]].ColB := Col.B; - end; - - if (copy(Theme.Score.TextNotesScore[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextNotesScore[I + 1 + Max]].ColR := Col.R; - Text[TextNotesScore[I + 1 + Max]].ColG := Col.G; - Text[TextNotesScore[I + 1 + Max]].ColB := Col.B; - end; - - if (copy(Theme.Score.TextLineBonus[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextLineBonus[I + 1 + Max]].ColR := Col.R; - Text[TextLineBonus[I + 1 + Max]].ColG := Col.G; - Text[TextLineBonus[I + 1 + Max]].ColB := Col.B; - end; - - if (copy(Theme.Score.TextLineBonusScore[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextLineBonusScore[I + 1 + Max]].ColR := Col.R; - Text[TextLineBonusScore[I + 1 + Max]].ColG := Col.G; - Text[TextLineBonusScore[I + 1 + Max]].ColB := Col.B; - end; - - if (copy(Theme.Score.TextGoldenNotes[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextGoldenNotes[I + 1 + Max]].ColR := Col.R; - Text[TextGoldenNotes[I + 1 + Max]].ColG := Col.G; - Text[TextGoldenNotes[I + 1 + Max]].ColB := Col.B; - end; - - if (copy(Theme.Score.TextGoldenNotesScore[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextGoldenNotesScore[I + 1 + Max]].ColR := Col.R; - Text[TextGoldenNotesScore[I + 1 + Max]].ColG := Col.G; - Text[TextGoldenNotesScore[I + 1 + Max]].ColB := Col.B; - end; - - if (copy(Theme.Score.TextTotal[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then + for I := 0 to High(PlayerStatic[P]) do begin - Text[TextTotal[I + 1 + Max]].ColR := Col.R; - Text[TextTotal[I + 1 + Max]].ColG := Col.G; - Text[TextTotal[I + 1 + Max]].ColB := Col.B; + Statics[PlayerStatic[P, I]].Texture := PlayerStaticTextures[P, I, J + 1].Tex; end; - if (copy(Theme.Score.TextTotalScore[I + 1 + Max].Color, 1, 2) = 'P' + IntToStr(I + 1)) then - begin - Text[TextTotalScore[I + 1 + Max]].ColR := Col.R; - Text[TextTotalScore[I + 1 + Max]].ColG := Col.G; - Text[TextTotalScore[I + 1 + Max]].ColB := Col.B; - end; - if((PlayersPlay > Max) and (Screen = 2)) then - begin - Statics[AvatarStaticRef[PlayersPlay-Max+I+1]].Visible:=true; - end - else if((PlayersPlay > Max) and (Screen = 1)) then - begin - Statics[AvatarStaticRef[PlayersPlay-Max+I+1]].Visible:=false; - end; + Statics[StaticBoxLightest[P]].Texture := PlayerBoxTextures[P, 0, J + 1].Tex; + Statics[StaticBoxLight[P]].Texture := PlayerBoxTextures[P, 1, J + 1].Tex; + Statics[StaticBoxDark[P]].Texture := PlayerBoxTextures[P, 2, J + 1].Tex; + ApplyScoreSlotPlayerVisuals(Self, P, J + 1); end; - { to keep it simple we just swap all statics, not just the shown ones } - for P := Low(PlayerStatic) to High(PlayerStatic) do - for I := 0 to High(PlayerStatic[P]) do - begin - Statics[PlayerStatic[P, I]].Texture := PlayerStaticTextures[P, I, Screen].Tex; - if (Theme.Score.PlayerStatic[P, I].Typ <> Texture_Type_Colorized) then - begin - J := FindPlayerIndexForThemeSlot(P, Screen); - if (J <> -1) and (J <= High(Ini.PlayerColor)) then - begin - Col := GetPlayerColor(Ini.PlayerColor[J]); - Statics[PlayerStatic[P, I]].Texture.ColR := Col.R; - Statics[PlayerStatic[P, I]].Texture.ColG := Col.G; - Statics[PlayerStatic[P, I]].Texture.ColB := Col.B; - end; - end; - end; - - { box statics } - for P := Low(PlayerStatic) to High(PlayerStatic) do + for P := 1 to PlayersPlay do begin - Statics[StaticBoxLightest[P]].Texture := PlayerBoxTextures[P, 0, Screen].Tex; - Statics[StaticBoxLight[P]].Texture := PlayerBoxTextures[P, 1, Screen].Tex; - Statics[StaticBoxDark[P]].Texture := PlayerBoxTextures[P, 2, Screen].Tex; + Statics[AvatarStaticRef[P]].Visible := + (PlayerPositionMap[P-1].Position > 0) and + (Screen = PlayerPositionMap[P-1].Screen); end; end; end; @@ -858,9 +865,10 @@ constructor TScreenScore.Create; Player: integer; Counter: integer; I: integer; - R, G, B: real; - Col2: integer; - ArrayStartModifier: integer; + ColDark: TRGB; + ColLight: TRGB; + ColLightest: TRGB; + SlotIndex: integer; begin inherited Create; @@ -911,22 +919,19 @@ constructor TScreenScore.Create; //## the bars that visualize the score ## //NoteBar ScoreBar - LoadColor(R, G, B, 'P' + IntToStr(Player) + 'Dark'); - Col2 := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - Tex_Score_NoteBarLevel_Dark[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Dark'), TEXTURE_TYPE_COLORIZED, Col2); - Tex_Score_NoteBarRound_Dark[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Dark_Round'), TEXTURE_TYPE_COLORIZED, Col2); + ColDark := GetPlayerColor(Ini.PlayerColor[Player - 1]); + Tex_Score_NoteBarLevel_Dark[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Dark'), TEXTURE_TYPE_COLORIZED, RGBFloatToInt(ColDark.R, ColDark.G, ColDark.B)); + Tex_Score_NoteBarRound_Dark[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Dark_Round'), TEXTURE_TYPE_COLORIZED, RGBFloatToInt(ColDark.R, ColDark.G, ColDark.B)); //LineBonus ScoreBar - LoadColor(R, G, B, 'P' + IntToStr(Player) + 'Light'); - Col2 := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - Tex_Score_NoteBarLevel_Light[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Light'), TEXTURE_TYPE_COLORIZED, Col2); - Tex_Score_NoteBarRound_Light[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Light_Round'), TEXTURE_TYPE_COLORIZED, Col2); + ColLight := GetPlayerLightColor(Ini.PlayerColor[Player - 1]); + Tex_Score_NoteBarLevel_Light[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Light'), TEXTURE_TYPE_COLORIZED, RGBFloatToInt(ColLight.R, ColLight.G, ColLight.B)); + Tex_Score_NoteBarRound_Light[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Light_Round'), TEXTURE_TYPE_COLORIZED, RGBFloatToInt(ColLight.R, ColLight.G, ColLight.B)); //GoldenNotes ScoreBar - LoadColor(R, G, B, 'P' + IntToStr(Player) + 'Lightest'); - Col2 := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - Tex_Score_NoteBarLevel_Lightest[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Lightest'), TEXTURE_TYPE_COLORIZED, Col2); - Tex_Score_NoteBarRound_Lightest[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Lightest_Round'), TEXTURE_TYPE_COLORIZED, Col2); + ColLightest := ColorSqrt(ColLight); + Tex_Score_NoteBarLevel_Lightest[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Lightest'), TEXTURE_TYPE_COLORIZED, RGBFloatToInt(ColLightest.R, ColLightest.G, ColLightest.B)); + Tex_Score_NoteBarRound_Lightest[Player] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Lightest_Round'), TEXTURE_TYPE_COLORIZED, RGBFloatToInt(ColLightest.R, ColLightest.G, ColLightest.B)); //textures aPlayerScoreScreenTextures[Player].Score_NoteBarLevel_Dark := Tex_Score_NoteBarLevel_Dark[Player]; @@ -939,65 +944,31 @@ constructor TScreenScore.Create; aPlayerScoreScreenTextures[Player].Score_NoteBarRound_Lightest := Tex_Score_NoteBarRound_Lightest[Player]; end; - //TODO: adapt for players 7 to 12 - // avatars - case PlayersPlay of - 1: ArrayStartModifier := 0; - 2: ArrayStartModifier := 1; - 3: ArrayStartModifier := 3; - 4: begin - if (Screens = 1) then - ArrayStartModifier := 0 - else - ArrayStartModifier := 1; - end; - 6: begin - if (Screens = 1) then - ArrayStartModifier := 0 - else - ArrayStartModifier := 3; - end; - else - ArrayStartModifier := 0; //this should never happen - end; + MapPlayersToPosition; for I := 1 to PlayersPlay do begin - if((Screens = 2) and (PlayersPlay > 3) and (I > Trunc(PlayersPlay/2))) then - begin - AvatarStatic[I + ArrayStartModifier] := AddStatic(Theme.Score.AvatarStatic[I-Trunc(PlayersPlay/2) + ArrayStartModifier]); - Statics[AvatarStatic[I + ArrayStartModifier]].Texture := AvatarPlayerTextures[I]; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.X := Theme.Score.AvatarStatic[I-Trunc(PlayersPlay/2) + ArrayStartModifier].X; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.Y := Theme.Score.AvatarStatic[I-Trunc(PlayersPlay/2) + ArrayStartModifier].Y; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.H := Theme.Score.AvatarStatic[I-Trunc(PlayersPlay/2) + ArrayStartModifier].H; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.W := Theme.Score.AvatarStatic[I-Trunc(PlayersPlay/2) + ArrayStartModifier].W; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.Z := Theme.Score.AvatarStatic[I-Trunc(PlayersPlay/2) + ArrayStartModifier].Z; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.Alpha := Theme.Score.AvatarStatic[I-Trunc(PlayersPlay/2) + ArrayStartModifier].Alpha; - end - else - begin - AvatarStatic[I + ArrayStartModifier] := AddStatic(Theme.Score.AvatarStatic[I + ArrayStartModifier]); - Statics[AvatarStatic[I + ArrayStartModifier]].Texture := AvatarPlayerTextures[I]; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.X := Theme.Score.AvatarStatic[I + ArrayStartModifier].X; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.Y := Theme.Score.AvatarStatic[I + ArrayStartModifier].Y; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.H := Theme.Score.AvatarStatic[I + ArrayStartModifier].H; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.W := Theme.Score.AvatarStatic[I + ArrayStartModifier].W; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.Z := Theme.Score.AvatarStatic[I + ArrayStartModifier].Z; - Statics[AvatarStatic[I + ArrayStartModifier]].Texture.Alpha := Theme.Score.AvatarStatic[I + ArrayStartModifier].Alpha; - end; - Statics[AvatarStatic[I + ArrayStartModifier]].Visible := true; - AvatarStaticRef[I]:=AvatarStatic[I + ArrayStartModifier]; + SlotIndex := PlayerPositionMap[I - 1].Position; + AvatarStaticRef[I] := AddStatic(Theme.Score.AvatarStatic[SlotIndex]); + Statics[AvatarStaticRef[I]].Texture := AvatarPlayerTextures[I]; + Statics[AvatarStaticRef[I]].Texture.X := Theme.Score.AvatarStatic[SlotIndex].X; + Statics[AvatarStaticRef[I]].Texture.Y := Theme.Score.AvatarStatic[SlotIndex].Y; + Statics[AvatarStaticRef[I]].Texture.H := Theme.Score.AvatarStatic[SlotIndex].H; + Statics[AvatarStaticRef[I]].Texture.W := Theme.Score.AvatarStatic[SlotIndex].W; + Statics[AvatarStaticRef[I]].Texture.Z := Theme.Score.AvatarStatic[SlotIndex].Z; + Statics[AvatarStaticRef[I]].Texture.Alpha := Theme.Score.AvatarStatic[SlotIndex].Alpha; + Statics[AvatarStaticRef[I]].Visible := true; + ApplyScoreSlotPlayerVisuals(Self, SlotIndex, I); end; StaticNavigate := AddStatic(Theme.Score.StaticNavigate); TextNavigate := AddText(Theme.Score.TextNavigate); - if (PlayersPlay <= 3) or (Screens = 2) then + if Screens = 2 then LoadSwapTextures; - //TODO: adapt for players 4 to 12 - //Send Buttons - for I := 1 to 3 do + // Send buttons are chosen from the current column layout at runtime. + for I := 1 to High(ButtonSend) do ButtonSend[I] := AddButton(Theme.Score.ButtonSend[I]); end; @@ -1018,59 +989,19 @@ destructor TScreenScore.Destroy; inherited; end; -//TODO: adapt for players 7 to 12 procedure TScreenScore.MapPlayersToPosition; - var - ArrayStartModifier: integer; - PlayersPerScreen: integer; - I: integer; +var + I: integer; + ScreenIndex: integer; begin - // all statics / texts are loaded at start - so that we have them all even if we change the amount of players - // To show the corrects statics / text from the them, we simply modify the start of the according arrays - // 1 Player -> Player[0].Score (The score for one player starts at 0) - // -> Statics[1] (The statics for the one player screen start at 1) - // 2 Player -> Player[0..1].Score - // -> Statics[2..3] - // 3 Player -> Player[0..5].Score - // -> Statics[4..6] - case PlayersPlay of - 1: ArrayStartModifier := 1; - 2, 4: ArrayStartModifier := 2; - 3, 6: ArrayStartModifier := 4; - else - ArrayStartModifier := 0; //this should never happen - end; - - if (PlayersPlay <= 3) or ((PlayersPlay > 3) and (Screens = 1)) then - PlayersPerScreen := PlayersPlay - else - PlayersPerScreen := PlayersPlay div 2; - SetLength(PlayerPositionMap, PlayersPlay); - // actually map players to positions - if (PlayersPlay <= 3) or (Screens = 2) then - begin - for I := 0 to PlayersPlay - 1 do - begin - PlayerPositionMap[I].Screen := (I div PlayersPerScreen) + 1; - if (PlayerPositionMap[I].Screen > Screens) then - PlayerPositionMap[I].Position := 0 - else - PlayerPositionMap[I].Position := ArrayStartModifier + (I mod PlayersPerScreen); - PlayerPositionMap[I].BothScreens := (PlayersPlay <= 3) and (Screens > 1); - end; - end - else + for I := 0 to PlayersPlay - 1 do begin - for I := 0 to PlayersPlay - 1 do - begin - PlayerPositionMap[I].Screen := 0; - PlayerPositionMap[I].Position := I + 1; - PlayerPositionMap[I].BothScreens := true; - end; + ScreenIndex := GetPlayerScreen(I, PlayersPlay, Screens); + PlayerPositionMap[I].Screen := ScreenIndex; + PlayerPositionMap[I].Position := GetScoreSlotIndex(I, PlayersPlay, Screens); end; - end; procedure TScreenScore.UpdateAnimation; @@ -1110,7 +1041,7 @@ procedure TScreenScore.DrawPlayerBars; begin for I := 0 to PlayersPlay - 1 do begin - if (PlayerPositionMap[I].Position > 0) and ((ScreenAct = PlayerPositionMap[I].Screen) or (PlayerPositionMap[I].BothScreens)) then + if (PlayerPositionMap[I].Position > 0) and (ScreenAct = PlayerPositionMap[I].Screen) then begin if (BarScore_EaseOut_Step >= (EaseOut_MaxSteps * 10)) then begin @@ -1173,82 +1104,14 @@ procedure TScreenScore.OnShow; Text[TextTitle].Text := CurrentSong.Title; Text[TextArtistTitle].Text := CurrentSong.Artist + ' - ' + CurrentSong.Title; - //TODO: adapt for players 7 to 12 - // set visibility - case PlayersPlay of - 1: begin - V[1] := true; - V[2] := false; - V[3] := false; - V[4] := false; - V[5] := false; - V[6] := false; - end; - 2, 4: begin - if (PlayersPlay = 2) or ((PlayersPlay = 4) and (Screens = 2)) then - begin - V[1] := false; - V[2] := true; - V[3] := true; - V[4] := false; - V[5] := false; - V[6] := false; - end - else - begin - V[1] := true; - V[2] := true; - V[3] := true; - V[4] := true; - V[5] := false; - V[6] := false; - end; - end; - 3, 6: begin - if (PlayersPlay = 3) or ((PlayersPlay = 6) and (Screens = 2)) then - begin - V[1] := false; - V[2] := false; - V[3] := false; - V[4] := true; - V[5] := true; - V[6] := true; - end - else - begin - V[1] := true; - V[2] := true; - V[3] := true; - V[4] := true; - V[5] := true; - V[6] := true; - end; - end; - end; + FillChar(V, SizeOf(V), 0); + for P := 0 to PlayersPlay - 1 do + if PlayerPositionMap[P].Position > 0 then + V[PlayerPositionMap[P].Position] := true; for P := 1 to UIni.IMaxPlayerCount do begin - Text[TextName[P]].Visible := V[P]; - Text[TextScore[P]].Visible := V[P]; - - Text[TextNotes[P]].Visible := V[P]; - Text[TextNotesScore[P]].Visible := V[P]; - Text[TextLineBonus[P]].Visible := V[P]; - Text[TextLineBonusScore[P]].Visible := V[P]; - Text[TextGoldenNotes[P]].Visible := V[P]; - Text[TextGoldenNotesScore[P]].Visible := V[P]; - Text[TextTotal[P]].Visible := V[P]; - Text[TextTotalScore[P]].Visible := V[P]; - - for I := 0 to high(PlayerStatic[P]) do - Statics[PlayerStatic[P, I]].Visible := V[P]; - - for I := 0 to high(PlayerTexts[P]) do - Text[PlayerTexts[P, I]].Visible := V[P]; - - Statics[StaticBoxLightest[P]].Visible := V[P]; - Statics[StaticBoxLight[P]].Visible := V[P]; - Statics[StaticBoxDark[P]].Visible := V[P]; + SetScoreSlotVisible(Self, P, V[P]); // we draw that on our own Statics[StaticBackLevel[P]].Visible := false; @@ -1257,6 +1120,9 @@ procedure TScreenScore.OnShow; Statics[StaticLevelRound[P]].Visible := false; end; + for P := 1 to PlayersPlay do + ApplyScoreSlotPlayerVisuals(Self, PlayerPositionMap[P - 1].Position, P); + for I := 0 to 2 do begin Button[I].Visible := false; @@ -1266,20 +1132,9 @@ procedure TScreenScore.OnShow; // Show Send Score Buttons if (ScreenSing.SungToEnd) and (Length(DllMan.Websites) > 0) then begin - case PlayersPlay of - 1: begin - Button[0].Visible := true; - Button[0].Selectable := true; - end; - 2,4: begin - Button[1].Visible := true; - Button[1].Selectable := true; - end; - 3,6: begin - Button[2].Visible := true; - Button[2].Selectable := true; - end; - end; + I := GetScoreButtonLayout(PlayersPlay, Screens) - 1; + Button[I].Visible := true; + Button[I].Selectable := true; end; Interaction := -1; @@ -1317,21 +1172,7 @@ procedure TScreenScore.ResetScores; end; for P := 1 to UIni.IMaxPlayerCount do - begin - // We set alpha to 0 , so we can nicely blend them in when we need them - Text[TextScore[P]].Alpha := 0; - Text[TextNotesScore[P]].Alpha := 0; - Text[TextNotes[P]].Alpha := 0; - Text[TextLineBonus[P]].Alpha := 0; - Text[TextLineBonusScore[P]].Alpha := 0; - Text[TextGoldenNotes[P]].Alpha := 0; - Text[TextGoldenNotesScore[P]].Alpha := 0; - Text[TextTotal[P]].Alpha := 0; - Text[TextTotalScore[P]].Alpha := 0; - Statics[StaticBoxLightest[P]].Texture.Alpha := 0; - Statics[StaticBoxLight[P]].Texture.Alpha := 0; - Statics[StaticBoxDark[P]].Texture.Alpha := 0; - end; + SetScoreSlotScoreAlpha(Self, P, 0); BarScore_EaseOut_Step := 1; BarPhrase_EaseOut_Step := 1; @@ -1418,7 +1259,7 @@ function TScreenScore.Draw: boolean; // we have to swap the themeobjects values on every draw // to support dual screen - for PlayerCounter := 1 to PlayersPlay do //TODO: adapt for players 7 to 12 + for PlayerCounter := 1 to PlayersPlay do begin FillPlayerItems(PlayerCounter); end; @@ -1429,40 +1270,40 @@ function TScreenScore.Draw: boolean; procedure TscreenScore.FillPlayerItems(PlayerNumber: integer); var - ThemeIndex: integer; + SlotIndex: integer; begin - ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position; - if (ThemeIndex > 0) and ((ScreenAct = PlayerPositionMap[PlayerNumber-1].Screen) or (PlayerPositionMap[PlayerNumber-1].BothScreens)) then + SlotIndex := PlayerPositionMap[PlayerNumber-1].Position; + if (SlotIndex > 0) and (ScreenAct = PlayerPositionMap[PlayerNumber-1].Screen) then begin - Text[TextName[ThemeIndex]].Text := Player[PlayerNumber-1].Name; + Text[TextName[SlotIndex]].Text := Player[PlayerNumber-1].Name; // end todo //golden - Text[TextGoldenNotesScore[ThemeIndex]].Text := IntToStr(TextGolden_ActualValue[PlayerNumber]); - Text[TextGoldenNotesScore[ThemeIndex]].Alpha := (BarGolden_EaseOut_Step / 100); + Text[TextGoldenNotesScore[SlotIndex]].Text := IntToStr(TextGolden_ActualValue[PlayerNumber]); + Text[TextGoldenNotesScore[SlotIndex]].Alpha := (BarGolden_EaseOut_Step / 100); - Statics[StaticBoxLightest[ThemeIndex]].Texture.Alpha := (BarGolden_EaseOut_Step / 100); - Text[TextGoldenNotes[ThemeIndex]].Alpha := (BarGolden_EaseOut_Step / 100); + Statics[StaticBoxLightest[SlotIndex]].Texture.Alpha := (BarGolden_EaseOut_Step / 100); + Text[TextGoldenNotes[SlotIndex]].Alpha := (BarGolden_EaseOut_Step / 100); // line bonus - Text[TextLineBonusScore[ThemeIndex]].Text := IntToStr(TextPhrase_ActualValue[PlayerNumber]); - Text[TextLineBonusScore[ThemeIndex]].Alpha := (BarPhrase_EaseOut_Step / 100); + Text[TextLineBonusScore[SlotIndex]].Text := IntToStr(TextPhrase_ActualValue[PlayerNumber]); + Text[TextLineBonusScore[SlotIndex]].Alpha := (BarPhrase_EaseOut_Step / 100); - Statics[StaticBoxLight[ThemeIndex]].Texture.Alpha := (BarPhrase_EaseOut_Step / 100); - Text[TextLineBonus[ThemeIndex]].Alpha := (BarPhrase_EaseOut_Step / 100); + Statics[StaticBoxLight[SlotIndex]].Texture.Alpha := (BarPhrase_EaseOut_Step / 100); + Text[TextLineBonus[SlotIndex]].Alpha := (BarPhrase_EaseOut_Step / 100); // plain score - Text[TextNotesScore[ThemeIndex]].Text := IntToStr(TextScore_ActualValue[PlayerNumber]); - Text[TextNotes[ThemeIndex]].Alpha := (BarScore_EaseOut_Step / 100); + Text[TextNotesScore[SlotIndex]].Text := IntToStr(TextScore_ActualValue[PlayerNumber]); + Text[TextNotes[SlotIndex]].Alpha := (BarScore_EaseOut_Step / 100); - Statics[StaticBoxDark[ThemeIndex]].Texture.Alpha := (BarScore_EaseOut_Step / 100); - Text[TextNotesScore[ThemeIndex]].Alpha := (BarScore_EaseOut_Step / 100); + Statics[StaticBoxDark[SlotIndex]].Texture.Alpha := (BarScore_EaseOut_Step / 100); + Text[TextNotesScore[SlotIndex]].Alpha := (BarScore_EaseOut_Step / 100); // total score - Text[TextTotalScore[ThemeIndex]].Text := IntToStr(TextScore_ActualValue[PlayerNumber] + TextPhrase_ActualValue[PlayerNumber] + TextGolden_ActualValue[PlayerNumber]); - Text[TextTotalScore[ThemeIndex]].Alpha := (BarScore_EaseOut_Step / 100); + Text[TextTotalScore[SlotIndex]].Text := IntToStr(TextScore_ActualValue[PlayerNumber] + TextPhrase_ActualValue[PlayerNumber] + TextGolden_ActualValue[PlayerNumber]); + Text[TextTotalScore[SlotIndex]].Alpha := (BarScore_EaseOut_Step / 100); - Text[TextTotal[ThemeIndex]].Alpha := (BarScore_EaseOut_Step / 100); + Text[TextTotal[SlotIndex]].Alpha := (BarScore_EaseOut_Step / 100); if(BarGolden_EaseOut_Step = 100) then begin @@ -1474,50 +1315,50 @@ procedure TscreenScore.FillPlayerItems(PlayerNumber: integer); procedure TScreenScore.ShowRating(PlayerNumber: integer); var Rating: integer; - ThemeIndex: integer; + SlotIndex: integer; begin - ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position; - if (ThemeIndex > 0) and ((ScreenAct = PlayerPositionMap[PlayerNumber-1].Screen) or (PlayerPositionMap[PlayerNumber-1].BothScreens)) then + SlotIndex := PlayerPositionMap[PlayerNumber-1].Position; + if (SlotIndex > 0) and (ScreenAct = PlayerPositionMap[PlayerNumber-1].Screen) then begin case (Player[PlayerNumber-1].ScoreTotalInt) of 0..2009: begin - Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_TONE_DEAF'); + Text[TextScore[SlotIndex]].Text := Language.Translate('SING_SCORE_TONE_DEAF'); Rating := 0; end; 2010..4009: begin - Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_AMATEUR'); + Text[TextScore[SlotIndex]].Text := Language.Translate('SING_SCORE_AMATEUR'); Rating := 1; end; 4010..5009: begin - Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_WANNABE'); + Text[TextScore[SlotIndex]].Text := Language.Translate('SING_SCORE_WANNABE'); Rating := 2; end; 5010..6009: begin - Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_HOPEFUL'); + Text[TextScore[SlotIndex]].Text := Language.Translate('SING_SCORE_HOPEFUL'); Rating := 3; end; 6010..7509: begin - Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_RISING_STAR'); + Text[TextScore[SlotIndex]].Text := Language.Translate('SING_SCORE_RISING_STAR'); Rating := 4; end; 7510..8509: begin - Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_LEAD_SINGER'); + Text[TextScore[SlotIndex]].Text := Language.Translate('SING_SCORE_LEAD_SINGER'); Rating := 5; end; 8510..9009: begin - Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_SUPERSTAR'); + Text[TextScore[SlotIndex]].Text := Language.Translate('SING_SCORE_SUPERSTAR'); Rating := 6; end; 9010..10000: begin - Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_ULTRASTAR'); + Text[TextScore[SlotIndex]].Text := Language.Translate('SING_SCORE_ULTRASTAR'); Rating := 7; end; else @@ -1525,9 +1366,9 @@ procedure TScreenScore.ShowRating(PlayerNumber: integer); end; //todo: this could break if the width is not given, for instance when there's a skin with no picture for ratings - if ( Theme.Score.StaticRatings[ThemeIndex].W > 0 ) and ( aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue > 0 ) then + if ( Theme.Score.StaticRatings[SlotIndex].W > 0 ) and ( aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue > 0 ) then begin - Text[TextScore[ThemeIndex]].Alpha := aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue / Theme.Score.StaticRatings[ThemeIndex].W; + Text[TextScore[SlotIndex]].Alpha := aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue / Theme.Score.StaticRatings[SlotIndex].W; end; // end todo @@ -1540,14 +1381,14 @@ procedure TscreenScore.DrawRating(PlayerNumber: integer; Rating: integer); Posx: real; Posy: real; Width: real; - ThemeIndex: integer; + SlotIndex: integer; begin - ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position; + SlotIndex := PlayerPositionMap[PlayerNumber-1].Position; - if (Theme.Score.StaticRatings[ThemeIndex].W <> 0) and (Theme.Score.StaticRatings[ThemeIndex].H <> 0) then + if (Theme.Score.StaticRatings[SlotIndex].W <> 0) and (Theme.Score.StaticRatings[SlotIndex].H <> 0) then begin - PosX := Theme.Score.StaticRatings[ThemeIndex].X + (Theme.Score.StaticRatings[ThemeIndex].W * 0.5); - PosY := Theme.Score.StaticRatings[ThemeIndex].Y + (Theme.Score.StaticRatings[ThemeIndex].H * 0.5); ; + PosX := Theme.Score.StaticRatings[SlotIndex].X + (Theme.Score.StaticRatings[SlotIndex].W * 0.5); + PosY := Theme.Score.StaticRatings[SlotIndex].Y + (Theme.Score.StaticRatings[SlotIndex].H * 0.5); ; Width := aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue/2; @@ -1577,12 +1418,12 @@ function TscreenScore.CalculateBouncing(PlayerNumber: integer): real; RaiseStep, MaxVal: real; EaseOut_Step: integer; - ThemeIndex: integer; + SlotIndex: integer; begin - ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position; + SlotIndex := PlayerPositionMap[PlayerNumber-1].Position; EaseOut_Step := aPlayerScoreScreenRatings[PlayerNumber].RateEaseStep; - MaxVal := Theme.Score.StaticRatings[ThemeIndex].W; + MaxVal := Theme.Score.StaticRatings[SlotIndex].W; RaiseStep := EaseOut_Step; @@ -1619,10 +1460,10 @@ procedure TscreenScore.EaseBarIn(PlayerNumber: integer; BarType: TScoreBarType); lTmp: real; Score: integer; - ThemeIndex: integer; + SlotIndex: integer; begin - ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position; - MaxHeight := Theme.Score.StaticBackLevel[ThemeIndex].H; + SlotIndex := PlayerPositionMap[PlayerNumber-1].Position; + MaxHeight := Theme.Score.StaticBackLevel[SlotIndex].H; // let's get the points according to the bar we draw // score array starts at 0, which means the score for player 1 is in score[0] @@ -1631,19 +1472,19 @@ procedure TscreenScore.EaseBarIn(PlayerNumber: integer; BarType: TScoreBarType); begin Score := Player[PlayerNumber - 1].ScoreInt; RaiseStep := BarScore_EaseOut_Step; - BarStartPosY := Theme.Score.StaticBackLevel[ThemeIndex].Y + MaxHeight; + BarStartPosY := Theme.Score.StaticBackLevel[SlotIndex].Y + MaxHeight; end else if (BarType = sbtLine) then begin Score := Player[PlayerNumber - 1].ScoreLineInt; RaiseStep := BarPhrase_EaseOut_Step; - BarStartPosY := Theme.Score.StaticBackLevel[ThemeIndex].Y - aPlayerScoreScreenDatas[PlayerNumber].BarScore_ActualHeight + MaxHeight; + BarStartPosY := Theme.Score.StaticBackLevel[SlotIndex].Y - aPlayerScoreScreenDatas[PlayerNumber].BarScore_ActualHeight + MaxHeight; end else if (BarType = sbtGolden) then begin Score := Player[PlayerNumber - 1].ScoreGoldenInt; RaiseStep := BarGolden_EaseOut_Step; - BarStartPosY := Theme.Score.StaticBackLevel[ThemeIndex].Y - aPlayerScoreScreenDatas[PlayerNumber].BarScore_ActualHeight - aPlayerScoreScreenDatas[PlayerNumber].BarLine_ActualHeight + MaxHeight; + BarStartPosY := Theme.Score.StaticBackLevel[SlotIndex].Y - aPlayerScoreScreenDatas[PlayerNumber].BarScore_ActualHeight - aPlayerScoreScreenDatas[PlayerNumber].BarLine_ActualHeight + MaxHeight; end; // the height dependend of the score @@ -1680,13 +1521,13 @@ procedure TscreenScore.DrawBar(BarType: TScoreBarType; PlayerNumber: integer; Ba var Width: real; BarStartPosX: real; - ThemeIndex: integer; + SlotIndex: integer; begin - ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position; + SlotIndex := PlayerPositionMap[PlayerNumber-1].Position; // this is solely for better readability of the drawing - Width := Theme.Score.StaticBackLevel[ThemeIndex].W; - BarStartPosX := Theme.Score.StaticBackLevel[ThemeIndex].X; + Width := Theme.Score.StaticBackLevel[SlotIndex].W; + BarStartPosX := Theme.Score.StaticBackLevel[SlotIndex].X; glColor4f(1, 1, 1, 1); @@ -1726,8 +1567,8 @@ procedure TscreenScore.DrawBar(BarType: TScoreBarType; PlayerNumber: integer; Ba glEnable(GL_BLEND); glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex3f(BarStartPosX, (BarStartPosY - Statics[StaticLevelRound[ThemeIndex]].Texture.h) - NewHeight, ZBars); - glTexCoord2f(1, 0); glVertex3f(BarStartPosX + Width, (BarStartPosY - Statics[StaticLevelRound[ThemeIndex]].Texture.h) - NewHeight, ZBars); + glTexCoord2f(0, 0); glVertex3f(BarStartPosX, (BarStartPosY - Statics[StaticLevelRound[SlotIndex]].Texture.h) - NewHeight, ZBars); + glTexCoord2f(1, 0); glVertex3f(BarStartPosX + Width, (BarStartPosY - Statics[StaticLevelRound[SlotIndex]].Texture.h) - NewHeight, ZBars); glTexCoord2f(1, 1); glVertex3f(BarStartPosX + Width, BarStartPosY - NewHeight, ZBars); glTexCoord2f(0, 1); glVertex3f(BarStartPosX, BarStartPosY - NewHeight, ZBars); glEnd; From eaa7c12e8989cb8a463be8a22414c5e2920a3939 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:32:13 +0200 Subject: [PATCH 08/11] Use generic sing lane layout --- src/base/UDraw.pas | 594 ++++++++++++---------------------- src/base/UGraphicClasses.pas | 126 ++------ src/base/USingScores.pas | 611 +++++++++-------------------------- 3 files changed, 373 insertions(+), 958 deletions(-) diff --git a/src/base/UDraw.pas b/src/base/UDraw.pas index 691aa4f93..f25eea231 100644 --- a/src/base/UDraw.pas +++ b/src/base/UDraw.pas @@ -38,7 +38,8 @@ interface UThemes, sdl2, UGraphicClasses, - UIni; + UIni, + UPlayerLayout; procedure SingDraw; procedure SingDrawLines; @@ -410,124 +411,98 @@ procedure SingDrawJukeboxBlackBackground; end; procedure SingDrawOscilloscopes; -begin; - if PlayersPlay = 1 then - SingDrawOscilloscope(Theme.Sing.Solo1PP1.Oscilloscope, 0); - - if PlayersPlay = 2 then + function GetBaseOscilloscopePosition: TThemePosition; begin - SingDrawOscilloscope(Theme.Sing.Solo2PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Solo2PP2.Oscilloscope, 1); + Result := Theme.Sing.PlayerTemplate.Oscilloscope; end; - - if PlayersPlay = 3 then + function GetBaseSingPlayerTemplate: TThemeSingPlayer; begin - if (CurrentSong.isDuet) then - begin - SingDrawOscilloscope(Theme.Sing.Duet3PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Duet3PP2.Oscilloscope, 1); - SingDrawOscilloscope(Theme.Sing.Duet3PP3.Oscilloscope, 2); - end - else - begin - SingDrawOscilloscope(Theme.Sing.Solo3PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Solo3PP2.Oscilloscope, 1); - SingDrawOscilloscope(Theme.Sing.Solo3PP3.Oscilloscope, 2); - end; + Result := Theme.Sing.PlayerTemplate; end; - - if PlayersPlay = 4 then + procedure GetLaneLayout(const PlayerCountOnScreen, PlayerIndexOnScreen: integer; + out LaneLeft, LaneRight, LaneTop, LaneWidth: integer); + var + Layout: TSingLaneLayout; begin - if (Ini.Screens = 1) then - begin - if ScreenAct = 1 then - begin - SingDrawOscilloscope(Theme.Sing.Solo2PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Solo2PP2.Oscilloscope, 1); - end; - if ScreenAct = 2 then - begin - SingDrawOscilloscope(Theme.Sing.Solo2PP1.Oscilloscope, 2); - SingDrawOscilloscope(Theme.Sing.Solo2PP2.Oscilloscope, 3); - end; - end - else - begin - if (CurrentSong.isDuet) then - begin - SingDrawOscilloscope(Theme.Sing.Duet4PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Duet4PP2.Oscilloscope, 1); - SingDrawOscilloscope(Theme.Sing.Duet4PP3.Oscilloscope, 2); - SingDrawOscilloscope(Theme.Sing.Duet4PP4.Oscilloscope, 3); - end - else - begin - SingDrawOscilloscope(Theme.Sing.Solo4PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Solo4PP2.Oscilloscope, 1); - SingDrawOscilloscope(Theme.Sing.Solo4PP3.Oscilloscope, 2); - SingDrawOscilloscope(Theme.Sing.Solo4PP4.Oscilloscope, 3); - end; - end; + Layout := GetSingLaneLayout(PlayerCountOnScreen, PlayerIndexOnScreen, Theme.Sing.PlayerLayout, + CurrentSong.isDuet and (PlayersPlay <> 1)); + LaneLeft := Layout.ColumnLeft; + LaneRight := Layout.ColumnRight; + LaneTop := Layout.RowAnchorY; + LaneWidth := Layout.ColumnWidth; end; - - if PlayersPlay = 6 then + function GetOscilloscopePosition(PlayerIndex: integer): TThemePosition; + var + BaseTemplate: TThemeSingPlayer; + BasePosition: TThemePosition; + LocalPlayerCount: integer; + LocalIndex: integer; + LaneLeft: integer; + LaneRight: integer; + LaneTop: integer; + LaneWidth: integer; + Scale: real; + FrameW: integer; + FrameH: integer; + ScoreW: integer; + ScoreH: integer; + NameX: integer; + NameY: integer; + NameW: integer; + GroupTop: integer; + HeaderOffsetLeft: integer; + Layout: TSingLaneLayout; begin - if (Ini.Screens = 1) then + if Screens > 1 then begin - if (CurrentSong.isDuet) then - begin - if ScreenAct = 1 then - begin - SingDrawOscilloscope(Theme.Sing.Duet3PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Duet3PP2.Oscilloscope, 1); - SingDrawOscilloscope(Theme.Sing.Duet3PP3.Oscilloscope, 2); - end; - if ScreenAct = 2 then - begin - SingDrawOscilloscope(Theme.Sing.Duet3PP1.Oscilloscope, 3); - SingDrawOscilloscope(Theme.Sing.Duet3PP2.Oscilloscope, 4); - SingDrawOscilloscope(Theme.Sing.Duet3PP3.Oscilloscope, 5); - end; - end - else - begin - if ScreenAct = 1 then - begin - SingDrawOscilloscope(Theme.Sing.Solo3PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Solo3PP2.Oscilloscope, 1); - SingDrawOscilloscope(Theme.Sing.Solo3PP3.Oscilloscope, 2); - end; - - if ScreenAct = 2 then - begin - SingDrawOscilloscope(Theme.Sing.Solo3PP1.Oscilloscope, 3); - SingDrawOscilloscope(Theme.Sing.Solo3PP2.Oscilloscope, 4); - SingDrawOscilloscope(Theme.Sing.Solo3PP3.Oscilloscope, 5); - end; - end; + LocalPlayerCount := GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct); + LocalIndex := GetPlayerIndexOnScreen(PlayerIndex, PlayersPlay, Screens); end else begin - if (CurrentSong.isDuet) then - begin - SingDrawOscilloscope(Theme.Sing.Duet6PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Duet6PP2.Oscilloscope, 1); - SingDrawOscilloscope(Theme.Sing.Duet6PP3.Oscilloscope, 2); - SingDrawOscilloscope(Theme.Sing.Duet6PP4.Oscilloscope, 3); - SingDrawOscilloscope(Theme.Sing.Duet6PP5.Oscilloscope, 4); - SingDrawOscilloscope(Theme.Sing.Duet6PP6.Oscilloscope, 5); - end - else - begin - SingDrawOscilloscope(Theme.Sing.Solo6PP1.Oscilloscope, 0); - SingDrawOscilloscope(Theme.Sing.Solo6PP2.Oscilloscope, 1); - SingDrawOscilloscope(Theme.Sing.Solo6PP3.Oscilloscope, 2); - SingDrawOscilloscope(Theme.Sing.Solo6PP4.Oscilloscope, 3); - SingDrawOscilloscope(Theme.Sing.Solo6PP5.Oscilloscope, 4); - SingDrawOscilloscope(Theme.Sing.Solo6PP6.Oscilloscope, 5); - end; + LocalPlayerCount := PlayersPlay; + LocalIndex := PlayerIndex; end; + + BaseTemplate := GetBaseSingPlayerTemplate; + BasePosition := GetBaseOscilloscopePosition; + Layout := GetSingLaneLayout(LocalPlayerCount, LocalIndex, Theme.Sing.PlayerLayout, + CurrentSong.isDuet and (PlayersPlay <> 1)); + GetLaneLayout(LocalPlayerCount, LocalIndex, LaneLeft, LaneRight, LaneTop, LaneWidth); + Scale := Layout.WidgetScale; + + FrameW := Max(Theme.Sing.PlayerWidgetLayout.MinFrameW, Round(BaseTemplate.AvatarFrame.W * Scale)); + FrameH := Max(Theme.Sing.PlayerWidgetLayout.MinFrameH, Round(BaseTemplate.AvatarFrame.H * Scale)); + ScoreW := Max(Theme.Sing.PlayerWidgetLayout.MinScoreW, Round(BaseTemplate.ScoreBackground.W * Scale)); + ScoreH := Max(Theme.Sing.PlayerWidgetLayout.MinScoreH, Round(BaseTemplate.ScoreBackground.H * Scale)); + HeaderOffsetLeft := Round(Theme.Sing.PlayerWidgetLayout.HeaderOffsetLeft * Scale); + GroupTop := Max(10, LaneTop - + GetSingHeaderTopOffset(Theme.Sing.PlayerWidgetLayout, Layout.GridRows, Scale)); + NameX := Max(0, LaneLeft - HeaderOffsetLeft) + FrameW + + Max(Theme.Sing.PlayerWidgetLayout.NameGapMinX, Round(Theme.Sing.PlayerWidgetLayout.NameGapBaseX * Scale)); + NameW := Max(Theme.Sing.PlayerWidgetLayout.NameMinW, + (LaneRight - ScoreW - Max(Theme.Sing.PlayerWidgetLayout.NameGapMinX, + Round(Theme.Sing.PlayerWidgetLayout.NameGapBaseX * Scale))) - NameX); + NameY := GroupTop + Max(0, (FrameH - Max(12, Round(BaseTemplate.Name.Size * Scale))) div 2); + NameX := Max(0, NameX - Max(Theme.Sing.PlayerWidgetLayout.NamePaddingMinX, + Round(Theme.Sing.PlayerWidgetLayout.NamePaddingBaseX * Scale))); + NameY := Max(0, NameY - Max(Theme.Sing.PlayerWidgetLayout.NamePaddingMinY, + Round(Theme.Sing.PlayerWidgetLayout.NamePaddingBaseY * Scale))); + + Result := BasePosition; + Result.X := NameX; + Result.Y := NameY + Max(12, Round(BaseTemplate.Name.H * Scale)) + + Max(Theme.Sing.PlayerWidgetLayout.OscilloscopeGapMinY, + Round(Theme.Sing.PlayerWidgetLayout.OscilloscopeGapBaseY * Scale)); + Result.W := Min(NameW, Max(Theme.Sing.PlayerWidgetLayout.OscilloscopeMinW, Round(BasePosition.W * Scale))); + Result.H := Max(Theme.Sing.PlayerWidgetLayout.OscilloscopeMinH, Round(BasePosition.H * Scale)); end; +var + PlayerIndex: integer; +begin; + for PlayerIndex := 0 to PlayersPlay - 1 do + if (Screens <= 1) or (GetPlayerScreen(PlayerIndex, PlayersPlay, Screens) = ScreenAct) then + SingDrawOscilloscope(GetOscilloscopePosition(PlayerIndex), PlayerIndex); end; procedure SingDrawOscilloscope(Position: TThemePosition; NrSound: integer); @@ -853,6 +828,9 @@ procedure SingDrawPlayerBGLine(Left, Top, Right: real; Track, PlayerIndex: integ Count: integer; TempR: real; W, H: real; + GlowPadX: real; + GlowExtraW: real; + GlowExtraH: real; begin if (ScreenSing.settings.NotesVisible[PlayerIndex]) then begin @@ -873,10 +851,11 @@ procedure SingDrawPlayerBGLine(Left, Top, Right: real; Track, PlayerIndex: integ begin if NoteType <> ntFreestyle then begin - // begin: 14, 20 - // easy: 6, 11 - W := NotesW[PlayerIndex] * 2 + 2; - H := NotesH[PlayerIndex] * 1.5 + 3.5; + GlowPadX := Max(1.0, NotesW[PlayerIndex] * 0.65); + GlowExtraW := Max(1.0, NotesW[PlayerIndex] * 0.35); + GlowExtraH := Max(1.5, NotesH[PlayerIndex] * 0.3); + W := NotesW[PlayerIndex] * 2 + GlowExtraW; + H := NotesH[PlayerIndex] * 1.5 + GlowExtraH; { X2 := (Start-CurrentSong.Tracks[Track].Lines[CurrentSong.Tracks[Track].Current].Notes[0].Start) * TempR + Left + 0.5 + 4; @@ -887,7 +866,7 @@ procedure SingDrawPlayerBGLine(Left, Top, Right: real; Track, PlayerIndex: integ } // left - Rec.Right := (StartBeat - CurrentSong.Tracks[Track].Lines[CurrentSong.Tracks[Track].CurrentLine].Notes[0].StartBeat) * TempR + Left + 0.5 + 4; + Rec.Right := (StartBeat - CurrentSong.Tracks[Track].Lines[CurrentSong.Tracks[Track].CurrentLine].Notes[0].StartBeat) * TempR + Left + 0.5 + GlowPadX; Rec.Left := Rec.Right - W; Rec.Top := Top - (Tone-BaseNote)*LineSpacing/2 - H; Rec.Bottom := Rec.Top + 2 * H; @@ -909,7 +888,7 @@ procedure SingDrawPlayerBGLine(Left, Top, Right: real; Track, PlayerIndex: integ // middle part Rec.Left := Rec.Right; - Rec.Right := (StartBeat + Duration - CurrentSong.Tracks[Track].Lines[CurrentSong.Tracks[Track].CurrentLine].Notes[0].StartBeat) * TempR + Left - 0.5 - 4; + Rec.Right := (StartBeat + Duration - CurrentSong.Tracks[Track].Lines[CurrentSong.Tracks[Track].CurrentLine].Notes[0].StartBeat) * TempR + Left - 0.5 - GlowPadX; // the left note is more right than the right note itself, sounds weird - so we fix that xD if Rec.Right <= Rec.Left then @@ -1065,26 +1044,10 @@ procedure SingDrawLyricHelper(CP: integer; Left, LyricsMid: real); if (CurrentSong.isDuet) then begin - if (PlayersPlay = 1) or (PlayersPlay = 2) then - Col := GetLyricBarColor(Ini.SingColor[CP]) + if (Screens > 1) and (GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct) = 2) then + Col := GetLyricBarColor(Ini.SingColor[GetFirstPlayerIndexForScreen(PlayersPlay, Screens, ScreenAct) + CP]) else - begin - if (PlayersPlay = 3) or (PlayersPlay = 6) then - begin - //if (PlayersPlay = 3) then - Col := GetLyricBarColor(Ini.SingColor[CP]); - - //if (PlayersPlay = 6) then - // Col := GetLyricBarColor(CP + 1); - end - else - begin - if ScreenAct = 1 then - Col := GetLyricBarColor(Ini.SingColor[CP]) - else - Col := GetLyricBarColor(Ini.SingColor[CP + 2]); - end; - end; + Col := GetLyricBarColor(Ini.SingColor[CP]); end else Col := GetLyricBarColor(1); @@ -1215,6 +1178,35 @@ procedure SingDrawLyricHelperJukebox(Left, LyricsMid: real); procedure SingDrawLines; var NR: TRecR; // lyrics area bounds (NR = NoteRec?) + PlayerIndex: integer; + LocalIndex: integer; + LineTop: real; + LineSpacing: integer; + PlayerCountOnScreen: integer; + LaneLeft: real; + LaneRight: real; + procedure GetLaneLayout(const CurrentPlayerIndex: integer; out Left, Right, Top: real; out Spacing: integer); + var + Layout: TSingLaneLayout; + begin + if Screens > 1 then + begin + PlayerCountOnScreen := GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct); + LocalIndex := GetPlayerIndexOnScreen(CurrentPlayerIndex, PlayersPlay, Screens); + end + else + begin + PlayerCountOnScreen := PlayersPlay; + LocalIndex := CurrentPlayerIndex; + end; + + Layout := GetSingLaneLayout(PlayerCountOnScreen, LocalIndex, Theme.Sing.PlayerLayout, + CurrentSong.isDuet and (PlayersPlay <> 1)); + Left := Layout.GridLeft; + Right := Layout.GridRight; + Top := Layout.GuideTopY; + Spacing := Layout.NoteLineSpacing; + end; begin // positions NR.Left := 20; @@ -1225,85 +1217,18 @@ procedure SingDrawLines; // draw note-lines - // to-do : needs fix when party mode works w/ 2 screens - if (PlayersPlay = 1) and (Ini.NoteLines = 1) and (ScreenSing.settings.NotesVisible[0]) then - SingDrawNoteLines(NR.Left, Skin_P2_NotesB - 105, NR.Right, 15); + if Ini.NoteLines <> 1 then + Exit; - if (PlayersPlay = 2) and (Ini.NoteLines = 1) then + for PlayerIndex := 0 to PlayersPlay - 1 do begin - if (ScreenSing.settings.NotesVisible[0]) then - SingDrawNoteLines(Nr.Left, Skin_P1_NotesB - 105, Nr.Right, 15); - if (ScreenSing.settings.NotesVisible[1]) then - SingDrawNoteLines(Nr.Left, Skin_P2_NotesB - 105, Nr.Right, 15); - end; + if (Screens > 1) and (GetPlayerScreen(PlayerIndex, PlayersPlay, Screens) <> ScreenAct) then + Continue; + if not ScreenSing.Settings.NotesVisible[PlayerIndex] then + Continue; - if (PlayersPlay = 3) and (Ini.NoteLines = 1) then begin - if (ScreenSing.settings.NotesVisible[0]) then - SingDrawNoteLines(Nr.Left, 120, Nr.Right, 12); - if (ScreenSing.settings.NotesVisible[1]) then - SingDrawNoteLines(Nr.Left, 245, Nr.Right, 12); - if (ScreenSing.settings.NotesVisible[2]) then - SingDrawNoteLines(Nr.Left, 370, Nr.Right, 12); - end; - - if (PlayersPlay = 4) and (Ini.NoteLines = 1) then - begin - if (ScreenSing.settings.NotesVisible[0]) then - begin - if (Ini.Screens = 1) then - SingDrawNoteLines(Nr.Left, Skin_P1_NotesB - 105, Nr.Right, 15) - else - begin - SingDrawNoteLines(Nr.Left, Skin_P1_NotesB - 105, Nr.Right/2 - 5, 15); - SingDrawNoteLines(Nr.Right/2 - 20 + Nr.Left, Skin_P1_NotesB - 105, Nr.Right, 15) - end; - end; - - if (ScreenSing.settings.NotesVisible[1]) then - begin - if (Ini.Screens = 1) then - SingDrawNoteLines(Nr.Left, Skin_P2_NotesB - 105, Nr.Right, 15) - else - begin - SingDrawNoteLines(Nr.Left, Skin_P2_NotesB - 105, Nr.Right/2 - 5, 15); - SingDrawNoteLines(Nr.Right/2 - 20 + Nr.Left, Skin_P2_NotesB - 105, Nr.Right, 15) - end; - end; - end; - - if (PlayersPlay = 6) and (Ini.NoteLines = 1) then begin - if (ScreenSing.settings.NotesVisible[0]) then - begin - if (Ini.Screens = 1) then - SingDrawNoteLines(Nr.Left, 120, Nr.Right, 12) - else - begin - SingDrawNoteLines(Nr.Left, 120, Nr.Right/2 - 5, 12); - SingDrawNoteLines(Nr.Right/2 - 20 + Nr.Left, 120, Nr.Right, 12); - end; - end; - - if (ScreenSing.settings.NotesVisible[1]) then - begin - if (Ini.Screens = 1) then - SingDrawNoteLines(Nr.Left, 245, Nr.Right, 12) - else - begin - SingDrawNoteLines(Nr.Left, 245, Nr.Right/2 - 5, 12); - SingDrawNoteLines(Nr.Right/2 - 20 + Nr.Left, 245, Nr.Right, 12); - end; - end; - - if (ScreenSing.settings.NotesVisible[2]) then - begin - if (Ini.Screens = 1) then - SingDrawNoteLines(Nr.Left, 370, Nr.Right, 12) - else - begin - SingDrawNoteLines(Nr.Left, 370, Nr.Right/2 - 5, 12); - SingDrawNoteLines(Nr.Right/2 - 20 + Nr.Left, 370, Nr.Right, 12); - end; - end; + GetLaneLayout(PlayerIndex, LaneLeft, LaneRight, LineTop, LineSpacing); + SingDrawNoteLines(LaneLeft, LineTop, LaneRight, LineSpacing); end; end; @@ -1315,18 +1240,40 @@ procedure SingDraw; LyricEngineDuetP2: TLyricEngine; I: integer; Difficulty: integer; - TrackP1, TrackP2, TrackP3, TrackP4, TrackP5, TrackP6: integer; -const - LineSpacingOneRow = 15; - LineSpacingTwoRows = 15; - LineSpacingThreeRows = 12; - // TODO: it looks like all these TopXRowsY constants are actually referring to the bottom. But all the functions they call have historically called it Top. - TopOneRow1 = Skin_P2_NotesB; - TopTwoRows1 = Skin_P1_NotesB; - TopTwoRows2 = Skin_P2_NotesB; - TopThreeRows1 = 120+95; - TopThreeRows2 = 245+95; - TopThreeRows3 = 370+95; + PlayerCountOnScreen: integer; + PlayerIndex: integer; + LocalIndex: integer; + LineTop: real; + LineSpacing: integer; + TrackIndex: integer; + LaneLeft: real; + LaneRight: real; + LaneWidth: real; + Layout: TSingLaneLayout; + ContentScale: real; + BaseNoteH: real; + BaseNoteW: real; + procedure GetLaneLayout(const CurrentPlayerIndex: integer; out Left, Right, Width, Top: real; out Spacing: integer); + begin + if Screens > 1 then + begin + PlayerCountOnScreen := GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct); + LocalIndex := GetPlayerIndexOnScreen(CurrentPlayerIndex, PlayersPlay, Screens); + end + else + begin + PlayerCountOnScreen := PlayersPlay; + LocalIndex := CurrentPlayerIndex; + end; + + Layout := GetSingLaneLayout(PlayerCountOnScreen, LocalIndex, Theme.Sing.PlayerLayout, + CurrentSong.isDuet and (PlayersPlay <> 1)); + Left := Layout.GridLeft; + Right := Layout.GridRight; + Width := Right - Left; + Top := Layout.RowAnchorY; + Spacing := Layout.NoteLineSpacing; + end; begin // positions NR.Left := 20; @@ -1334,21 +1281,11 @@ procedure SingDraw; NR.Width := 760; //NR.Right - NR.Left; NR.WMid := 380; //NR.Width / 2; NR.Mid := 400; //NR.Left + NR.WMid; - - TrackP1 := 0; - TrackP2 := 0; - TrackP3 := 0; - TrackP4 := 0; - TrackP5 := 0; - TrackP6 := 0; // FIXME: accessing ScreenSing is not that generic if (CurrentSong.isDuet) and (PlayersPlay <> 1) then begin LyricEngineDuetP1 := ScreenSing.LyricsDuetP1; LyricEngineDuetP2 := ScreenSing.LyricsDuetP2; - TrackP2 := 1; - TrackP4 := 1; - TrackP6 := 1; end else LyricEngine := ScreenSing.Lyrics; @@ -1379,7 +1316,6 @@ procedure SingDraw; for I := 1 to PlayersPlay do begin - if (ScreenSong.Mode = smNormal) or (ScreenSong.Mode = smMedley) then Difficulty := Player[I - 1].Level else @@ -1388,195 +1324,62 @@ procedure SingDraw; case Difficulty of 0: begin - NotesH[I - 1] := 11; // 9 - NotesW[I - 1] := 6; // 5 + BaseNoteH := 11; + BaseNoteW := 6; end; 1: begin - NotesH[I - 1] := 8; // 7 - NotesW[I - 1] := 4; // 4 + BaseNoteH := 8; + BaseNoteW := 4; end; 2: begin - NotesH[I - 1] := 5; - NotesW[I - 1] := 3; + BaseNoteH := 5; + BaseNoteW := 3; end; - end; - - if PlayersPlay = 3 then - begin - NotesW[I - 1] := NotesW[I - 1] * 0.8; - NotesH[I - 1] := NotesH[I - 1] * 0.8; - end; - - if PlayersPlay = 4 then - begin - if (Ini.Screens = 0) then + else begin - NotesW[I - 1] := NotesW[I - 1] * 0.9; + BaseNoteH := 8; + BaseNoteW := 4; end; end; - if PlayersPlay = 6 then + if Screens > 1 then begin - NotesW[I - 1] := NotesW[I - 1] * 0.8; - NotesH[I - 1] := NotesH[I - 1] * 0.8; + PlayerCountOnScreen := GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct); + LocalIndex := GetPlayerIndexOnScreen(I - 1, PlayersPlay, Screens); + end + else + begin + PlayerCountOnScreen := PlayersPlay; + LocalIndex := I - 1; end; + Layout := GetSingLaneLayout(PlayerCountOnScreen, LocalIndex, Theme.Sing.PlayerLayout, + CurrentSong.isDuet and (PlayersPlay <> 1)); + ContentScale := Layout.ContentScale; + NotesH[I - 1] := Max(2.0, BaseNoteH * ContentScale); + NotesW[I - 1] := Max(1.0, BaseNoteW * ContentScale); end; // draw notes lines if (ScreenSing.Settings.InputVisible) then SingDrawLines; // Draw the Notes - if (PlayersPlay = 1) then + for PlayerIndex := 0 to PlayersPlay - 1 do begin - // SINGLESCREEN - SingDrawPlayerBGLine(NR.Left + 20, TopOneRow1, NR.Right - 20, TrackP1, 0, LineSpacingOneRow); // Background glow - colorized in playercolor - SingDrawLine(NR.Left + 20, TopOneRow1, NR.Right - 20, TrackP1, 0, LineSpacingOneRow); // Plain unsung notes - colorized in playercolor - SingDrawPlayerLine(NR.Left + 20, TopOneRow1, NR.Width - 40, TrackP1, 0, LineSpacingOneRow); // imho the sung notes - end; + if (Screens > 1) and (GetPlayerScreen(PlayerIndex, PlayersPlay, Screens) <> ScreenAct) then + Continue; - if (PlayersPlay = 2) then - begin - // SINGLESCREEN - SingDrawPlayerBGLine(NR.Left + 20, TopTwoRows1, NR.Right - 20, TrackP1, 0, LineSpacingTwoRows); - SingDrawLine(NR.Left + 20, TopTwoRows1, NR.Right - 20, TrackP1, 0, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Left + 20, TopTwoRows1, NR.Width - 40, TrackP1, 0, LineSpacingTwoRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopTwoRows2, NR.Right - 20, TrackP2, 1, LineSpacingTwoRows); - SingDrawLine(NR.Left + 20, TopTwoRows2, NR.Right - 20, TrackP2, 1, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Left + 20, TopTwoRows2, NR.Width - 40, TrackP2, 1, LineSpacingTwoRows); - end; + GetLaneLayout(PlayerIndex, LaneLeft, LaneRight, LaneWidth, LineTop, LineSpacing); - if (PlayersPlay = 3) then - begin - // SINGLESCREEN - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows1, NR.Right - 20, TrackP1, 0, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows1, NR.Right - 20, TrackP1, 0, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows1, NR.Width - 40, TrackP1, 0, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows2, NR.Right - 20, TrackP2, 1, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows2, NR.Right - 20, TrackP2, 1, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows2, NR.Width - 40, TrackP2, 1, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows3, NR.Right - 20, TrackP3, 2, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows3, NR.Right - 20, TrackP3, 2, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows3, NR.Width - 40, TrackP3, 2, LineSpacingThreeRows); - end; + TrackIndex := 0; + if (CurrentSong.isDuet) and (PlayersPlay <> 1) and Odd(PlayerIndex) then + TrackIndex := 1; - if (PlayersPlay = 4) then - begin - if (Ini.Screens = 1) then - begin - // MULTISCREEN - if (ScreenAct = 1) then - begin - // MULTISCREEN - SCREEN 1 - SingDrawPlayerBGLine(NR.Left + 20, TopTwoRows1, NR.Right - 20, TrackP1, 0, LineSpacingTwoRows); - SingDrawLine(NR.Left + 20, TopTwoRows1, NR.Right - 20, TrackP1, 0, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Left + 20, TopTwoRows1, NR.Width - 40, TrackP1, 0, LineSpacingTwoRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopTwoRows2, NR.Right - 20, TrackP2, 1, LineSpacingTwoRows); - SingDrawLine(NR.Left + 20, TopTwoRows2, NR.Right - 20, TrackP2, 1, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Left + 20, TopTwoRows2, NR.Width - 40, TrackP2, 1, LineSpacingTwoRows); - end; - if (ScreenAct = 2) then - begin - // MULTISCREEN - SCREEN 2 - SingDrawPlayerBGLine(NR.Left + 20, TopTwoRows1, NR.Right - 20, TrackP3, 2, LineSpacingTwoRows); - SingDrawLine(NR.Left + 20, TopTwoRows1, NR.Right - 20, TrackP3, 2, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Left + 20, TopTwoRows1, NR.Width - 40, TrackP3, 2, LineSpacingTwoRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopTwoRows2, NR.Right - 20, TrackP4, 3, LineSpacingTwoRows); - SingDrawLine(NR.Left + 20, TopTwoRows2, NR.Right - 20, TrackP4, 3, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Left + 20, TopTwoRows2, NR.Width - 40, TrackP4, 3, LineSpacingTwoRows); - end; - end - else - begin - // SINGLESCREEN - SingDrawPlayerBGLine(NR.Left + 20, TopTwoRows1, NR.Right/2 - 20, TrackP1, 0, LineSpacingTwoRows); - SingDrawLine(NR.Left + 20, TopTwoRows1, NR.Right/2 - 20, TrackP1, 0, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Left + 20, TopTwoRows1, NR.Width/2 - 50, TrackP1, 0, LineSpacingTwoRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopTwoRows2, NR.Right/2 - 20, TrackP2, 1, LineSpacingTwoRows); - SingDrawLine(NR.Left + 20, TopTwoRows2, NR.Right/2 - 20, TrackP2, 1, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Left + 20, TopTwoRows2, NR.Width/2 - 50, TrackP2, 1, LineSpacingTwoRows); - - SingDrawPlayerBGLine(NR.Right/2 - 20 + NR.Left + 20, TopTwoRows1, NR.Right - 20, TrackP3, 2, LineSpacingTwoRows); - SingDrawLine(NR.Right/2 - 20 + NR.Left + 20, TopTwoRows1, NR.Right - 20, TrackP3, 2, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Width/2 - 10 + NR.Left + 20, TopTwoRows1, NR.Width/2 - 30, TrackP3, 2, LineSpacingTwoRows); - - SingDrawPlayerBGLine(NR.Right/2 - 20 + NR.Left + 20, TopTwoRows2, NR.Right - 20, TrackP4, 3, LineSpacingTwoRows); - SingDrawLine(NR.Right/2 - 20 + NR.Left + 20, TopTwoRows2, NR.Right - 20, TrackP4, 3, LineSpacingTwoRows); - SingDrawPlayerLine(NR.Width/2 - 10 + NR.Left + 20, TopTwoRows2, NR.Width/2 - 30, TrackP4, 3, LineSpacingTwoRows); - end; - end; - - if (PlayersPlay = 6) then - begin - if (Ini.Screens = 1) then - begin - // MULTISCREEN - if (ScreenAct = 1) then - begin - // MULTISCREEN - SCREEN 1 - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows1, NR.Right - 20, TrackP1, 0, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows1, NR.Right - 20, TrackP1, 0, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows1, NR.Width - 40, TrackP1, 0, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows2, NR.Right - 20, TrackP2, 1, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows2, NR.Right - 20, TrackP2, 1, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows2, NR.Width - 40, TrackP2, 1, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows3, NR.Right - 20, TrackP3, 2, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows3, NR.Right - 20, TrackP3, 2, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows3, NR.Width - 40, TrackP3, 2, LineSpacingThreeRows); - end; - if (ScreenAct = 2) then - begin - // MULTISCREEN - SCREEN 2 - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows1, NR.Right - 20, TrackP4, 3, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows1, NR.Right - 20, TrackP4, 3, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows1, NR.Width - 40, TrackP4, 3, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows2, NR.Right - 20, TrackP5, 4, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows2, NR.Right - 20, TrackP5, 4, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows2, NR.Width - 40, TrackP5, 4, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows3, NR.Right - 20, TrackP6, 5, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows3, NR.Right - 20, TrackP6, 5, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows3, NR.Width - 40, TrackP6, 5, LineSpacingThreeRows); - end; - end - else - begin - // SINGLESCREEN - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows1, NR.Right/2 - 20, TrackP1, 0, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows1, NR.Right/2 - 20, TrackP1, 0, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows1, NR.Width/2 - 50, TrackP1, 0, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows2, NR.Right/2 - 20, TrackP2, 1, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows2, NR.Right/2 - 20, TrackP2, 1, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows2, NR.Width/2 - 50, TrackP2, 1, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Left + 20, TopThreeRows3, NR.Right/2 - 20, TrackP3, 2, LineSpacingThreeRows); - SingDrawLine(NR.Left + 20, TopThreeRows3, NR.Right/2 - 20, TrackP3, 2, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Left + 20, TopThreeRows3, NR.Width/2 - 50, TrackP3, 2, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Right/2 - 20 + NR.Left + 20, TopThreeRows1, NR.Right - 20, TrackP4, 3, LineSpacingThreeRows); - SingDrawLine(NR.Right/2 - 20 + NR.Left + 20, TopThreeRows1, NR.Right - 20, TrackP4, 3, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Width/2 - 10 + NR.Left + 20, TopThreeRows1, NR.Width/2 - 30, TrackP4, 3, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Right/2 - 20 + NR.Left + 20, TopThreeRows2, NR.Right - 20, TrackP5, 4, LineSpacingThreeRows); - SingDrawLine(NR.Right/2 - 20 + NR.Left + 20, TopThreeRows2, NR.Right - 20, TrackP5, 4, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Width/2 - 10 + NR.Left + 20, TopThreeRows2, NR.Width/2 - 30, TrackP5, 4, LineSpacingThreeRows); - - SingDrawPlayerBGLine(NR.Right/2 - 20 + NR.Left + 20, TopThreeRows3, NR.Right - 20, TrackP6, 5, LineSpacingThreeRows); - SingDrawLine(NR.Right/2 - 20 + NR.Left + 20, TopThreeRows3, NR.Right - 20, TrackP6, 5, LineSpacingThreeRows); - SingDrawPlayerLine(NR.Width/2 - 10 + NR.Left + 20, TopThreeRows3, NR.Width/2 - 30, TrackP6, 5, LineSpacingThreeRows); - end; + SingDrawPlayerBGLine(LaneLeft, LineTop, LaneRight, TrackIndex, PlayerIndex, LineSpacing); + SingDrawLine(LaneLeft, LineTop, LaneRight, TrackIndex, PlayerIndex, LineSpacing); + SingDrawPlayerLine(LaneLeft, LineTop, LaneWidth, TrackIndex, PlayerIndex, LineSpacing); end; glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); @@ -1866,4 +1669,3 @@ procedure SingDrawJukeboxTimeBar(); end; end. - diff --git a/src/base/UGraphicClasses.pas b/src/base/UGraphicClasses.pas index 1a41faa9f..899dc6fc8 100644 --- a/src/base/UGraphicClasses.pas +++ b/src/base/UGraphicClasses.pas @@ -37,6 +37,7 @@ interface UTexture, ULog, UIni, + UPlayerLayout, SDL2; const @@ -665,109 +666,42 @@ procedure TEffectManager.SpawnPerfectLineTwinkle(); P, I, Life: cardinal; Left, Right, Top, Bottom: cardinal; cScreen, Nstars: integer; + ScreenCount: integer; + LocalPlayerCount: integer; + LocalPlayerIndex: integer; + SlotRect: TPlayerSlotRect; +const + NotesAreaLeft = 30; + NotesAreaTop = 130; + NotesAreaRight = 770; + NotesAreaBottom = 465; begin -// calculation of coordinates done with hardcoded values like in UDraw.pas -// might need to be adjusted if drawing of SingScreen is modified -// coordinates may still be a bit weird and need adjustment - Left := 30; - Right := 770; + ScreenCount := Ini.Screens + 1; // spawn effect for every player with a perfect line for P := 0 to PlayersPlay-1 do if Player[P].LastSentencePerfect then begin - // 3 and 6 players in 1 screen - if (Ini.Screens = 0) then - begin - if (PlayersPlay = 4) then - begin - if (P <= 1) then - begin - Left := 30; - Right := 385; - end - else - begin - Left := 415; - Right := 770; - end; - end; - - if (PlayersPlay = 6) then - begin - if (P <= 2) then - begin - Left := 30; - Right := 385; - end - else - begin - Left := 415; - Right := 770; - end; - end; - end; - - // calculate area where notes of this player are drawn - case PlayersPlay of - 1: begin - Bottom := Skin_P2_NotesB+10; - Top := Bottom-105; - cScreen := 1; - end; - 2,4: begin - case P of - 0,2: begin - Bottom := Skin_P1_NotesB+10; - Top := Bottom-105; - end; - else begin - Bottom := Skin_P2_NotesB+10; - Top := Bottom-105; - end; - end; - case P of - 0,1: cScreen := 1; - else - begin - if (Ini.Screens = 1) then - cScreen := 2 - else - cScreen := 1; - end; - end; - end; - 3,6: begin - case P of - 0,3: begin - Top := 130; - Bottom := Top+85; - end; - 1,4: begin - Top := 255; - Bottom := Top+85; - end; - 2,5: begin - Top := 380; - Bottom := Top+85; - end; - end; - case P of - 0,1,2: cScreen := 1; - else - begin - if (Ini.Screens = 1) then - cScreen := 2 - else - cScreen := 1; - end; - end; - end; - end; + cScreen := GetPlayerScreen(P, PlayersPlay, ScreenCount); + LocalPlayerCount := GetScreenPlayerCount(PlayersPlay, ScreenCount, cScreen); + LocalPlayerIndex := GetPlayerIndexOnScreen(P, PlayersPlay, ScreenCount); + SlotRect := GetPlayerSlotRect( + LocalPlayerIndex, + LocalPlayerCount, + NotesAreaLeft, + NotesAreaTop, + NotesAreaRight - NotesAreaLeft, + NotesAreaBottom - NotesAreaTop + ); + + Left := SlotRect.X; + Right := SlotRect.X + SlotRect.W; + Top := SlotRect.Y; + Bottom := SlotRect.Y + SlotRect.H; // spawn Sparkling Stars inside calculated coordinates Nstars := 80; - if (Ini.Screens = 0) and (PlayersPlay > 3) then + if LocalPlayerCount > 3 then Nstars := 40; for I := 0 to Nstars do @@ -775,12 +709,8 @@ procedure TEffectManager.SpawnPerfectLineTwinkle(); Life := RandomRange(8,16); Spawn(RandomRange(Left,Right), RandomRange(Top,Bottom), cScreen, Life, 16-Life, -1, PerfectLineTwinkle, P); - //spawn also on second screen if the amount of players is <=3 - if (Screens = 2) and (PlayersPlay <= 3) then - Spawn(RandomRange(Left,Right), RandomRange(Top,Bottom), 2, Life, 16-Life, -1, PerfectLineTwinkle, P); end; end; end; end. - diff --git a/src/base/USingScores.pas b/src/base/USingScores.pas index 749ddf3ca..10dd409b9 100644 --- a/src/base/USingScores.pas +++ b/src/base/USingScores.pas @@ -36,6 +36,8 @@ interface uses dglOpenGL, UCommon, + UIni, + UPlayerLayout, UThemes, UTexture; @@ -51,8 +53,8 @@ interface // some constants containing options that could change by time const - MaxPlayers = 6; // maximum of players that could be added - MaxPositions = 6; // maximum of score positions that could be added + MaxPlayers = UIni.IMaxPlayerCount; // maximum of players that could be added + MaxPositions = MaxPlayers; // maximum of score positions that could be added type //----------- @@ -242,6 +244,108 @@ implementation UNote, UGraphic; +function IsPlayerVisibleOnScreen(const EncodedPosition: byte; const Screen: integer): boolean; +begin + if EncodedPosition = High(byte) then + Exit(false); + + if Screens <= 1 then + Exit(true); + + Result := (((EncodedPosition and 128) = 0) = (Screen = 1)); +end; + +function GetScorePositionForPlayer(const PlayerIndex: integer): TScorePosition; +var + BaseTemplate: TThemeSingPlayer; + PlayerCountOnScreen: integer; + LocalIndex: integer; + LaneLeft: integer; + LaneRight: integer; + LaneWidth: integer; + Scale: real; + FrameH: integer; + ScoreH: integer; + ScoreScale: real; + ScoreOffsetX: integer; + ScoreOffsetY: integer; + RatingOffsetX: integer; + RatingOffsetY: integer; + PopupYOffset: integer; + PopupFontSize: integer; + GroupTop: real; + Layout: TSingLaneLayout; +begin + if Screens > 1 then + begin + PlayerCountOnScreen := GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct); + LocalIndex := GetPlayerIndexOnScreen(PlayerIndex, PlayersPlay, Screens); + end + else + begin + PlayerCountOnScreen := PlayersPlay; + LocalIndex := PlayerIndex; + end; + + Layout := GetSingLaneLayout(PlayerCountOnScreen, LocalIndex, Theme.Sing.PlayerLayout, + CurrentSong.isDuet and (PlayersPlay <> 1)); + LaneLeft := Layout.ColumnLeft; + LaneRight := Layout.ColumnRight; + LaneWidth := Layout.ColumnWidth; + + BaseTemplate := Theme.Sing.PlayerTemplate; + Scale := Layout.WidgetScale; + + ScoreScale := Min(Scale, (LaneWidth * Theme.Sing.PlayerWidgetLayout.ScoreWidthFraction) / + Max(1.0, BaseTemplate.ScoreBackground.W * 1.0)); + + FrameH := Max(Theme.Sing.PlayerWidgetLayout.MinFrameH, Round(BaseTemplate.AvatarFrame.H * Scale)); + ScoreH := Max(Theme.Sing.PlayerWidgetLayout.MinScoreH, Round(BaseTemplate.ScoreBackground.H * Scale)); + Result.PlayerCount := 0; + Result.BGW := Round(BaseTemplate.ScoreBackground.W * ScoreScale); + Result.BGH := Round(BaseTemplate.ScoreBackground.H * ScoreScale); + Result.BGX := LaneRight - Result.BGW; + GroupTop := Max(10, Layout.RowAnchorY - + GetSingHeaderTopOffset(Theme.Sing.PlayerWidgetLayout, Layout.GridRows, Scale)); + Result.BGY := GroupTop; + + ScoreOffsetX := BaseTemplate.Score.X - BaseTemplate.ScoreBackground.X; + ScoreOffsetY := BaseTemplate.Score.Y - BaseTemplate.ScoreBackground.Y; + Result.TextX := Result.BGX + Round(ScoreOffsetX * ScoreScale); + Result.TextY := Result.BGY + Round(ScoreOffsetY * ScoreScale); + Result.TextFont := BaseTemplate.Score.Font; + Result.TextStyle := BaseTemplate.Score.Style; + Result.TextSize := Max(1, Round(BaseTemplate.Score.Size * ScoreScale)); + + RatingOffsetX := BaseTemplate.SingBar.X - BaseTemplate.ScoreBackground.X; + RatingOffsetY := BaseTemplate.SingBar.Y - BaseTemplate.ScoreBackground.Y; + Result.RBX := Result.BGX + Round(RatingOffsetX * ScoreScale); + Result.RBY := Result.BGY + Round(RatingOffsetY * ScoreScale); + Result.RBW := Round(BaseTemplate.SingBar.W * ScoreScale); + Result.RBH := Round(BaseTemplate.SingBar.H * ScoreScale); + + if CurrentSong.isDuet then + begin + PopupYOffset := Theme.Sing.PlayerWidgetLayout.PopupYOffsetDuet; + PopupFontSize := Theme.Sing.PlayerWidgetLayout.PopupFontSizeDuet; + end + else + begin + PopupYOffset := Theme.Sing.PlayerWidgetLayout.PopupYOffsetSolo; + PopupFontSize := Theme.Sing.PlayerWidgetLayout.PopupFontSizeSolo; + end; + + Result.PUW := Result.BGW; + Result.PUH := Result.BGH; + Result.PUFont := 0; + Result.PUStyle := ftOutline; + Result.PUSize := Max(1, Round(PopupFontSize * Scale)); + Result.PUStartX := Result.BGX; + Result.PUStartY := Result.TextY + Round(PopupYOffset * Scale); + Result.PUTargetX := Result.BGX; + Result.PUTargetY := Result.TextY; +end; + {** * sets some standard settings *} @@ -358,43 +462,6 @@ procedure TSingScores.Clear; procedure TSingScores.LoadfromTheme; var I: integer; - procedure AddbyStatics(const PC: byte; const ScoreStatic: TThemePosition; const SingBarStatic: TThemePosition; ScoreText: TThemeText); - var - nPosition: TScorePosition; - begin - nPosition.PlayerCount := PC; // only for one player playing - - nPosition.BGX := ScoreStatic.X; - nPosition.BGY := ScoreStatic.Y; - nPosition.BGW := ScoreStatic.W; - nPosition.BGH := ScoreStatic.H; - - nPosition.TextX := ScoreText.X; - nPosition.TextY := ScoreText.Y; - nPosition.TextFont := ScoreText.Font; - nPosition.TextStyle := ScoreText.Style; - nPosition.TextSize := ScoreText.Size; - - nPosition.RBX := SingBarStatic.X; - nPosition.RBY := SingBarStatic.Y; - nPosition.RBW := SingBarStatic.W; - nPosition.RBH := SingBarStatic.H; - - nPosition.PUW := nPosition.BGW; - nPosition.PUH := nPosition.BGH; - - nPosition.PUFont := 0; - nPosition.PUStyle := ftOutline; - nPosition.PUSize := 18; - - nPosition.PUStartX := nPosition.BGX; - nPosition.PUStartY := nPosition.TextY + 65; - - nPosition.PUTargetX := nPosition.BGX; - nPosition.PUTargetY := nPosition.TextY; - - AddPosition(@nPosition); - end; begin Clear; @@ -407,21 +474,6 @@ procedure TSingScores.LoadfromTheme; Settings.RatingBar_BG_Tex := Tex_SingBar_Back; Settings.RatingBar_FG_Tex := Tex_SingBar_Front; Settings.RatingBar_Bar_Tex := Tex_SingBar_Bar; - - // load positions from theme - - // player 1: - AddByStatics(1, Theme.Sing.Solo1PP1.ScoreBackground, Theme.Sing.Solo1PP1.SingBar, Theme.Sing.Solo1PP1.Score); - AddByStatics(2, Theme.Sing.Solo2PP1.ScoreBackground, Theme.Sing.Solo2PP1.SingBar, Theme.Sing.Solo2PP1.Score); - AddByStatics(4, Theme.Sing.Solo3PP1.ScoreBackground, Theme.Sing.Solo3PP1.SingBar, Theme.Sing.Solo3PP1.Score); - - // player 2: - AddByStatics(2, Theme.Sing.Solo2PP2.ScoreBackground, Theme.Sing.Solo2PP2.SingBar, Theme.Sing.Solo2PP2.Score); - AddByStatics(4, Theme.Sing.Solo3PP2.ScoreBackground, Theme.Sing.Solo3PP2.SingBar, Theme.Sing.Solo3PP2.Score); - - // player 3: - AddByStatics(4, Theme.Sing.Solo3PP3.ScoreBackground, Theme.Sing.Solo3PP3.SingBar, Theme.Sing.Solo3PP3.Score); - end; {** @@ -581,104 +633,15 @@ function TSingScores.GetPopUpPoints(const Index: integer): integer; *} procedure TSingScores.Init; var - PlC: array [0..1] of byte; // playercount first screen and second screen - I, J: integer; - MaxPlayersperScreen: byte; - CurPlayer: byte; - - function GetPositionCountbyPlayerCount(bPlayerCount: byte): byte; - var - I: integer; - begin - Result := 0; - bPlayerCount := 1 shl (bPlayerCount - 1); - - for I := 0 to PositionCount - 1 do - begin - if ((aPositions[I].PlayerCount and bPlayerCount) <> 0) then - Inc(Result); - end; - end; - - function GetPositionbyPlayernum(bPlayerCount, bPlayer: byte): byte; - var - I: integer; - begin - bPlayerCount := 1 shl (bPlayerCount - 1); - Result := High(byte); - - for I := 0 to PositionCount - 1 do - begin - if ((aPositions[I].PlayerCount and bPlayerCount) <> 0) then - begin - if (bPlayer = 0) then - begin - Result := I; - Break; - end - else - Dec(bPlayer); - end; - end; - end; - + I: integer; begin - MaxPlayersPerScreen := 0; - - for I := 1 to 6 do + for I := 0 to PlayerCount - 1 do begin - // if there are enough positions -> write to maxplayers - if (Screens = 2) or (PlayersPlay <= 3) then - begin - if (GetPositionCountbyPlayerCount(I) = I) then - MaxPlayersPerScreen := I - else - Break; - end + if Screens > 1 then + aPlayers[I].Position := GetPlayerIndexOnScreen(I, PlayerCount, Screens) or ((GetPlayerScreen(I, PlayerCount, Screens) - 1) shl 7) else - begin - // DIRTY HACK for 4/6 players one screen - MaxPlayersPerScreen := PlayersPlay; - //oPlayerCount := PlayersPlay; - end; - end; - - // split players to both screens or display on one screen - if (Screens = 2) and (MaxPlayersPerScreen < PlayerCount) then - begin - PlC[0] := PlayerCount div 2 + PlayerCount mod 2; - PlC[1] := PlayerCount div 2; - end - else - begin - PlC[0] := PlayerCount; - PlC[1] := 0; - end; - - // check if there are enough positions for all players - for I := 0 to Screens - 1 do - begin - if (PlC[I] > MaxPlayersperScreen) then - begin - PlC[I] := MaxPlayersperScreen; - Log.LogError('More Players than available Positions, TSingScores'); - end; + aPlayers[I].Position := I; end; - - CurPlayer := 0; - // give every player a position - for I := 0 to Screens - 1 do - for J := 0 to PlC[I]-1 do - begin - // DIRTY HACK for 4/6 players one screen - if (Screens = 2) or (PlayersPlay <= 3) then - aPlayers[CurPlayer].Position := GetPositionbyPlayernum(PlC[I], J) or (I shl 7) - else - aPlayers[CurPlayer].Position := J; - - //Log.LogError('Player ' + InttoStr(CurPlayer) + ' gets Position: ' + InttoStr(aPlayers[CurPlayer].Position)); - Inc(CurPlayer); - end; end; {** @@ -782,36 +745,8 @@ procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); TextLen: real; ScoretoAdd: word; PosDiff: real; - procedure aPositionsInternal(index: integer; themeElements: TThemeSingPlayer); - var - yOffset: integer; - puSize: integer; - begin - if (CurrentSong.isDuet) then begin - yOffset := 40; - puSize := 14; - end else begin - yOffset := 65; - puSize := 18; - end; - aPositions[PIndex].PUSize := puSize; - - aPositions[PIndex].PUW := themeElements.ScoreBackground.W; - aPositions[PIndex].PUH := themeElements.ScoreBackground.H; - - aPositions[PIndex].PUStartX := themeElements.ScoreBackground.X; - aPositions[PIndex].PUStartY := themeElements.Score.Y + yOffset; - - aPositions[PIndex].PUTargetX := themeElements.ScoreBackground.X; - aPositions[PIndex].PUTargetY := themeElements.Score.Y; - - end; + Position: TScorePosition; begin -{ if screens = 2 and playerplay <= 3 the 2nd screen shows the - textures of screen 1 } - if (Screens = 2) and (PlayersPlay <= 3) then - ScreenAct := 1; - if (PopUp <> nil) then begin // only draw if player has a position @@ -819,7 +754,7 @@ procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); if PIndex <> High(byte) then begin // only draw if player is on cur screen - if ((Players[PopUp.Player].Position and 128) = 0) = (ScreenAct = 1) then + if IsPlayerVisibleOnScreen(Players[PopUp.Player].Position, ScreenAct) then begin CurTime := SDL_GetTicks; if not (Enabled and Players[PopUp.Player].Enabled) then @@ -831,85 +766,7 @@ procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); // get position of popup PIndex := PIndex and 127; - - // DIRTY HACK - // correct position for duet with 3/6 players and 4/6 players in one screen - if (Screens = 1) and ((PlayersPlay = 4) or (PlayersPlay = 6)) then - begin - if (PlayersPlay = 4) then - begin - if (CurrentSong.isDuet) then - begin - case (PopUp.Player) of - 0: aPositionsInternal(PIndex, Theme.Sing.Duet4PP1); - 1: aPositionsInternal(PIndex, Theme.Sing.Duet4PP2); - 2: aPositionsInternal(PIndex, Theme.Sing.Duet4PP3); - 3: aPositionsInternal(PIndex, Theme.Sing.Duet4PP4); - end; - end - else - begin - case (PopUp.Player) of - 0: aPositionsInternal(PIndex, Theme.Sing.Solo4PP1); - 1: aPositionsInternal(PIndex, Theme.Sing.Solo4PP2); - 2: aPositionsInternal(PIndex, Theme.Sing.Solo4PP3); - 3: aPositionsInternal(PIndex, Theme.Sing.Solo4PP4); - end; - end; - end; - - if (PlayersPlay = 6) then - begin - if (CurrentSong.isDuet) then - begin - case (PopUp.Player) of - 0: aPositionsInternal(PIndex, Theme.Sing.Duet6PP1); - 1: aPositionsInternal(PIndex, Theme.Sing.Duet6PP2); - 2: aPositionsInternal(PIndex, Theme.Sing.Duet6PP3); - 3: aPositionsInternal(PIndex, Theme.Sing.Duet6PP4); - 4: aPositionsInternal(PIndex, Theme.Sing.Duet6PP5); - 5: aPositionsInternal(PIndex, Theme.Sing.Duet6PP6); - end; - end - else - begin - case (PopUp.Player) of - 0: aPositionsInternal(0, Theme.Sing.Solo6PP1); - 1: aPositionsInternal(1, Theme.Sing.Solo6PP2); - 2: aPositionsInternal(2, Theme.Sing.Solo6PP3); - 3: aPositionsInternal(3, Theme.Sing.Solo6PP4); - 4: aPositionsInternal(4, Theme.Sing.Solo6PP5); - 5: aPositionsInternal(5, Theme.Sing.Solo6PP6); - end; - end; - end; - end - else - begin - - if (CurrentSong.isDuet) then - begin - if ((PlayersPlay = 3) or (PlayersPlay = 6)) then - begin - case (PopUp.Player) of - 0, 3, 6: aPositionsInternal(PIndex, Theme.Sing.Duet3PP1); - 1, 4, 7: aPositionsInternal(PIndex, Theme.Sing.Duet3PP2); - 2, 5, 8: aPositionsInternal(PIndex, Theme.Sing.Duet3PP3); - end; - end; - end - else - begin - if ((PlayersPlay = 3) or (PlayersPlay = 6)) then - begin - case (PopUp.Player) of - 0, 3, 6: aPositionsInternal(PIndex, Theme.Sing.Solo3PP1); - 1, 4, 7: aPositionsInternal(PIndex, Theme.Sing.Solo3PP2); - 2, 5, 8: aPositionsInternal(PIndex, Theme.Sing.Solo3PP3); - end; - end; - end; - end; + Position := GetScorePositionForPlayer(PopUp.Player); // check for phase ... if (TimeDiff <= Settings.Phase1Time) then @@ -918,13 +775,13 @@ procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); Progress := TimeDiff / Settings.Phase1Time; - W := aPositions[PIndex].PUW * Sin(Progress/2*Pi); - H := aPositions[PIndex].PUH * Sin(Progress/2*Pi); + W := Position.PUW * Sin(Progress/2*Pi); + H := Position.PUH * Sin(Progress/2*Pi); - X := aPositions[PIndex].PUStartX + (aPositions[PIndex].PUW - W)/2; - Y := aPositions[PIndex].PUStartY + (aPositions[PIndex].PUH - H)/2; + X := Position.PUStartX + (Position.PUW - W)/2; + Y := Position.PUStartY + (Position.PUH - H)/2; - FontSize := Round(Progress * aPositions[PIndex].PUSize); + FontSize := Round(Progress * Position.PUSize); FontOffset := (H - FontSize) / 2; Alpha := 1; end @@ -934,20 +791,20 @@ procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); // phase 2 - the moving Progress := (TimeDiff - Settings.Phase1Time) / Settings.Phase2Time; - W := aPositions[PIndex].PUW; - H := aPositions[PIndex].PUH; + W := Position.PUW; + H := Position.PUH; - PosDiff := aPositions[PIndex].PUTargetX - aPositions[PIndex].PUStartX; + PosDiff := Position.PUTargetX - Position.PUStartX; if PosDiff > 0 then PosDiff := PosDiff + W; - X := aPositions[PIndex].PUStartX + PosDiff * sqr(Progress); + X := Position.PUStartX + PosDiff * sqr(Progress); - PosDiff := aPositions[PIndex].PUTargetY - aPositions[PIndex].PUStartY; + PosDiff := Position.PUTargetY - Position.PUStartY; if PosDiff < 0 then - PosDiff := PosDiff + aPositions[PIndex].BGH; - Y := aPositions[PIndex].PUStartY + PosDiff * sqr(Progress); + PosDiff := PosDiff + Position.BGH; + Y := Position.PUStartY + PosDiff * sqr(Progress); - FontSize := aPositions[PIndex].PUSize; + FontSize := Position.PUSize; FontOffset := (H - FontSize) / 2; Alpha := 1 - 0.3 * Progress; end @@ -980,24 +837,24 @@ procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); // set positions etc. Alpha := 0.7 - 0.7 * Progress; - W := aPositions[PIndex].PUW; - H := aPositions[PIndex].PUH; + W := Position.PUW; + H := Position.PUH; - PosDiff := aPositions[PIndex].PUTargetX - aPositions[PIndex].PUStartX; + PosDiff := Position.PUTargetX - Position.PUStartX; if (PosDiff > 0) then PosDiff := W else PosDiff := 0; - X := aPositions[PIndex].PUTargetX + PosDiff * Progress; + X := Position.PUTargetX + PosDiff * Progress; - PosDiff := aPositions[PIndex].PUTargetY - aPositions[PIndex].PUStartY; + PosDiff := Position.PUTargetY - Position.PUStartY; if (PosDiff < 0) then - PosDiff := -aPositions[PIndex].BGH + PosDiff := -Position.BGH else PosDiff := 0; - Y := aPositions[PIndex].PUTargetY - PosDiff * (1 - Progress); + Y := Position.PUTargetY - PosDiff * (1 - Progress); - FontSize := aPositions[PIndex].PUSize; + FontSize := Position.PUSize; FontOffset := (H - FontSize) / 2; end else @@ -1034,8 +891,8 @@ procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); glDisable(GL_BLEND); // set font style and size - SetFontFamily(aPositions[PIndex].PUFont); - SetFontStyle(aPositions[PIndex].PUStyle); + SetFontFamily(Position.PUFont); + SetFontStyle(Position.PUStyle); SetFontItalic(false); SetFontSize(FontSize); SetFontReflection(false, 0); @@ -1065,106 +922,16 @@ procedure TSingScores.DrawScore(const Index: integer); Position: TScorePosition; ScoreStr: String; Drawing: boolean; - procedure updatePosition(themeElements: TThemeSingPlayer); - begin - Position.BGX := themeElements.ScoreBackground.X; - Position.BGY := themeElements.ScoreBackground.Y; - Position.BGW := themeElements.ScoreBackground.W; - Position.BGH := themeElements.ScoreBackground.H; - - Position.TextX := themeElements.Score.X; - Position.TextY := themeElements.Score.Y; - Position.TextFont := themeElements.Score.Font; - Position.TextStyle := themeElements.Score.Style; - Position.TextSize := themeElements.Score.Size; - end; begin Drawing := false; - { if screens = 2 and playerplay <= 3 the 2nd screen shows the - textures of screen 1 } - if (Screens = 2) and (PlayersPlay <= 3) then - ScreenAct := 1; - - // DIRTY HACK - // correct position for duet with 3/6 players and 4/6 players one screen - if (Screens = 1) and ((PlayersPlay = 4) or (PlayersPlay = 6)) then + if Players[Index].Position <> High(byte) then begin - - Position := aPositions[Players[Index].Position and 127]; - Drawing := true; - - if (PlayersPlay = 4) then - begin - if (CurrentSong.isDuet) then - begin - case Index of - 0: updatePosition(Theme.Sing.Duet4PP1); - 1: updatePosition(Theme.Sing.Duet4PP2); - 2: updatePosition(Theme.Sing.Duet4PP3); - 3: updatePosition(Theme.Sing.Duet4PP4); - end; - end - else - begin - case Index of - 0: updatePosition(Theme.Sing.Solo4PP1); - 1: updatePosition(Theme.Sing.Solo4PP2); - 2: updatePosition(Theme.Sing.Solo4PP3); - 3: updatePosition(Theme.Sing.Solo4PP4); - end; - end; - end - else + if IsPlayerVisibleOnScreen(Players[Index].Position, ScreenAct) and Players[Index].Visible then begin - // 6 players - if (CurrentSong.isDuet) then - begin - case Index of - 0: updatePosition(Theme.Sing.Duet6PP1); - 1: updatePosition(Theme.Sing.Duet6PP2); - 2: updatePosition(Theme.Sing.Duet6PP3); - 3: updatePosition(Theme.Sing.Duet6PP4); - 4: updatePosition(Theme.Sing.Duet6PP5); - 5: updatePosition(Theme.Sing.Duet6PP6); - end; - end - else - begin - case Index of - 0: updatePosition(Theme.Sing.Solo6PP1); - 1: updatePosition(Theme.Sing.Solo6PP2); - 2: updatePosition(Theme.Sing.Solo6PP3); - 3: updatePosition(Theme.Sing.Solo6PP4); - 4: updatePosition(Theme.Sing.Solo6PP5); - 5: updatePosition(Theme.Sing.Solo6PP6); - end; - end; + Position := GetScorePositionForPlayer(Index); + Drawing := true; end; - end - else - begin - - // only draw if player has a position - if Players[Index].Position <> High(byte) then - begin - // only draw if player is on cur screen - if (((Players[Index].Position and 128) = 0) = (ScreenAct = 1)) and Players[Index].Visible then - begin - Position := aPositions[Players[Index].Position and 127]; - - Drawing := true; - - if (CurrentSong.isDuet) and ((PlayersPlay = 3) or (PlayersPlay = 6)) then - begin - case Index of - 0, 3, 6: updatePosition(Theme.Sing.Duet3PP1); - 1, 4, 7: updatePosition(Theme.Sing.Duet3PP2); - 2, 5, 8: updatePosition(Theme.Sing.Duet3PP3); - end; - end; - end; - end; end; if (Drawing) then @@ -1210,101 +977,17 @@ procedure TSingScores.DrawRatingBar(const Index: integer); R, G, B: real; Size, Diff: real; Drawing: boolean; - procedure updatePosition(themeElements: TThemeSingPlayer); - begin - Position.RBX := themeElements.SingBar.X; - Position.RBY := themeElements.SingBar.Y; - Position.RBW := themeElements.SingBar.W; - Position.RBH := themeElements.SingBar.H; - end; begin - { if screens = 2 and playerplay <= 3 the 2nd screen shows the - textures of screen 1 } - if (Screens = 2) and (PlayersPlay <= 3) then - ScreenAct := 1; - Drawing := false; - // DIRTY HACK - // correct position for duet with 3/6 players and 4/6 players in one screen - if (Screens = 1) and ((PlayersPlay = 4) or (PlayersPlay = 6)) then + if Players[Index].Position <> High(byte) then begin - Drawing := true; - - if (PlayersPlay = 4) then + if IsPlayerVisibleOnScreen(Players[Index].Position, ScreenAct) and + Players[index].RBVisible and + Players[index].Visible then begin - - if (CurrentSong.isDuet) then - begin - case Index of - 0: updatePosition(Theme.Sing.Duet4PP1); - 1: updatePosition(Theme.Sing.Duet4PP2); - 2: updatePosition(Theme.Sing.Duet4PP3); - 3: updatePosition(Theme.Sing.Duet4PP4); - end; - end - else - begin - case Index of - 0: updatePosition(Theme.Sing.Solo4PP1); - 1: updatePosition(Theme.Sing.Solo4PP2); - 2: updatePosition(Theme.Sing.Solo4PP3); - 3: updatePosition(Theme.Sing.Solo4PP4); - end; - end; - end - else - begin - // 6 players - if (CurrentSong.isDuet) then - begin - case Index of - 0: updatePosition(Theme.Sing.Duet6PP1); - 1: updatePosition(Theme.Sing.Duet6PP2); - 2: updatePosition(Theme.Sing.Duet6PP3); - 3: updatePosition(Theme.Sing.Duet6PP4); - 4: updatePosition(Theme.Sing.Duet6PP5); - 5: updatePosition(Theme.Sing.Duet6PP6); - end; - end - else - begin - case Index of - 0: updatePosition(Theme.Sing.Solo6PP1); - 1: updatePosition(Theme.Sing.Solo6PP2); - 2: updatePosition(Theme.Sing.Solo6PP3); - 3: updatePosition(Theme.Sing.Solo6PP4); - 4: updatePosition(Theme.Sing.Solo6PP5); - 5: updatePosition(Theme.Sing.Solo6PP6); - end; - end; - end; - end - else - begin - // only draw if player has a position - if Players[Index].Position <> High(byte) then - begin - // only draw if player is on cur screen - if (((Players[Index].Position and 128) = 0) = (ScreenAct = 1) and - Players[index].RBVisible and - Players[index].Visible) then - begin - Drawing := true; - - Position := aPositions[Players[Index].Position and 127]; - - // DIRTY HACK - // correct position for duet with 3/6 players - if (CurrentSong.isDuet) and ((PlayersPlay = 3) or (PlayersPlay = 6)) then - begin - case Index of - 0, 3, 6: updatePosition(Theme.Sing.Duet3PP1); - 1, 4, 7: updatePosition(Theme.Sing.Duet3PP2); - 2, 5, 8: updatePosition(Theme.Sing.Duet3PP3); - end; - end; - end; + Drawing := true; + Position := GetScorePositionForPlayer(Index); end; end; From d8833a603f707baac47a9af72e9713b1fccd6dd9 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:32:42 +0200 Subject: [PATCH 09/11] Simplify sing controller player slots --- .../controllers/UScreenSingController.pas | 200 +----------------- src/screens/views/UScreenSingView.pas | 188 +++++++++++++++- 2 files changed, 188 insertions(+), 200 deletions(-) diff --git a/src/screens/controllers/UScreenSingController.pas b/src/screens/controllers/UScreenSingController.pas index da01af249..f2e32e8eb 100644 --- a/src/screens/controllers/UScreenSingController.pas +++ b/src/screens/controllers/UScreenSingController.pas @@ -689,7 +689,7 @@ destructor TScreenSingController.Destroy; procedure TScreenSingController.OnShow; var - BadPlayer: integer; + BadPlayer, I: integer; Col, ColP1, ColP2: TRGB; begin inherited; @@ -745,17 +745,6 @@ procedure TScreenSingController.OnShow; CheckPlayerConfigOnNextSong := false; end; - if (CurrentSong.isDuet) then - begin - if (PlayersPlay = 4) then - begin - screenSingViewRef.ColPlayer[0] := GetPlayerColor(Ini.PlayerColor[0]); - screenSingViewRef.ColPlayer[1] := GetPlayerColor(Ini.PlayerColor[1]); - screenSingViewRef.ColPlayer[2] := GetPlayerColor(Ini.PlayerColor[2]); - screenSingViewRef.ColPlayer[3] := GetPlayerColor(Ini.PlayerColor[3]); - end; - end; - // set custom options if (CurrentSong.isDuet) and (PlayersPlay <> 1) then begin @@ -1391,193 +1380,7 @@ procedure TScreenSingController.OnHide; end; function TScreenSingController.Draw: boolean; -var - V1: boolean; - V1TwoP: boolean; // position of score box in two player mode - V1ThreeP: boolean; // position of score box in three player mode - V2R: boolean; - V2M: boolean; - V3R: boolean; - VDuet1ThreeP: boolean; - VDuet2M: boolean; - VDuet3R: boolean; - V1FourP: boolean; - V2FourP: boolean; - V3FourP: boolean; - V4FourP: boolean; - V1SixP: boolean; - V2SixP: boolean; - V3SixP: boolean; - V4SixP: boolean; - V5SixP: boolean; - V6SixP: boolean; - V1DuetFourP: boolean; - V2DuetFourP: boolean; - V3DuetFourP: boolean; - V4DuetFourP: boolean; - V1DuetSixP: boolean; - V2DuetSixP: boolean; - V3DuetSixP: boolean; - V4DuetSixP: boolean; - V5DuetSixP: boolean; - V6DuetSixP: boolean; begin - V1 := false; - V1TwoP := false; - V1ThreeP := false; - V2R := false; - V2M := false; - V3R := false; - - VDuet1ThreeP := false; - VDuet2M := false; - VDuet3R := false; - - V1FourP := false; - V2FourP := false; - V3FourP := false; - V4FourP := false; - - V1SixP := false; - V2SixP := false; - V3SixP := false; - V4SixP := false; - V5SixP := false; - V6SixP := false; - - V1DuetFourP := false; - V2DuetFourP := false; - V3DuetFourP := false; - V4DuetFourP := false; - - V1DuetSixP := false; - V2DuetSixP := false; - V3DuetSixP := false; - V4DuetSixP := false; - V5DuetSixP := false; - V6DuetSixP := false; - - case PlayersPlay of - 1: - begin - V1 := true; - end; - 2: - begin - V1TwoP := true; - V2R := true; - end; - 3: - begin - if (CurrentSong.isDuet) then - begin - VDuet1ThreeP := true; - VDuet2M := true; - VDuet3R := true; - end - else - begin - V1ThreeP := true; - V2M := true; - V3R := true; - end; - end; - 4: - begin // double screen - if (Ini.Screens = 1) then - begin - V1TwoP := true; - V2R := true; - end - else - begin - if (CurrentSong.isDuet) then - begin - V1DuetFourP := true; - V2DuetFourP := true; - V3DuetFourP := true; - V4DuetFourP := true; - end - else - begin - V1FourP := true; - V2FourP := true; - V3FourP := true; - V4FourP := true; - end; - end; - end; - 6: - begin // double screen - if (Ini.Screens = 1) then - begin - if (CurrentSong.isDuet) then - begin - VDuet1ThreeP := true; - VDuet2M := true; - VDuet3R := true; - end - else - begin - V1ThreeP := true; - V2M := true; - V3R := true; - end; - end - else - begin - if (CurrentSong.isDuet) then - begin - V1DuetSixP := true; - V2DuetSixP := true; - V3DuetSixP := true; - V4DuetSixP := true; - V5DuetSixP := true; - V6DuetSixP := true; - end - else - begin - V1SixP := true; - V2SixP := true; - V3SixP := true; - V4SixP := true; - V5SixP := true; - V6SixP := true; - end; - end; - end; - end; - - Text[screenSingViewRef.TextP1].Visible := V1 and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP1TwoP].Visible := V1TwoP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP2R].Visible := V2R and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP1ThreeP].Visible := V1ThreeP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP2M].Visible := V2M and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP3R].Visible := V3R and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextDuetP1ThreeP].Visible := VDuet1ThreeP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextDuetP2M].Visible := VDuet2M and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextDuetP3R].Visible := VDuet3R and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP1FourP].Visible := V1FourP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP2FourP].Visible := V2FourP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP3FourP].Visible := V3FourP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP4FourP].Visible := V4FourP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP1SixP].Visible := V1SixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP2SixP].Visible := V2SixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP3SixP].Visible := V3SixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP4SixP].Visible := V4SixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP5SixP].Visible := V5SixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP6SixP].Visible := V6SixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP1DuetFourP].Visible := V1DuetFourP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP2DuetFourP].Visible := V2DuetFourP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP3DuetFourP].Visible := V3DuetFourP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP4DuetFourP].Visible := V4DuetFourP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP1DuetSixP].Visible := V1DuetSixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP2DuetSixP].Visible := V2DuetSixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP3DuetSixP].Visible := V3DuetSixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP4DuetSixP].Visible := V4DuetSixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP5DuetSixP].Visible := V5DuetSixP and ScreenSing.Settings.AvatarsVisible; - Text[screenSingViewRef.TextP6DuetSixP].Visible := V6DuetSixP and ScreenSing.Settings.AvatarsVisible; - Result := screenSingViewRef.Draw(); end; @@ -2014,4 +1817,3 @@ procedure TScreenSingController.AutoSaveScore; end; end. - diff --git a/src/screens/views/UScreenSingView.pas b/src/screens/views/UScreenSingView.pas index 2dfd15d19..959e4c42e 100644 --- a/src/screens/views/UScreenSingView.pas +++ b/src/screens/views/UScreenSingView.pas @@ -756,6 +756,7 @@ function TScreenSingView.Draw: boolean; DisplayPrefix: string; DisplayMin: integer; DisplaySec: integer; + I: integer; T: integer; CurLyricsTime: real; VideoFrameTime: Extended; @@ -763,6 +764,185 @@ function TScreenSingView.Draw: boolean; medley_end: boolean; medley_start_applause: boolean; LastLineSungToEnd: boolean; + procedure SetPlayerNameVisible(TextIndex: integer; Visible: boolean); + begin + ScreenSing.Text[TextIndex].Visible := Visible and ScreenSing.Settings.AvatarsVisible; + end; + procedure UpdatePlayerNameVisibility; + var + V1: boolean; + V1TwoP: boolean; + V1ThreeP: boolean; + V2R: boolean; + V2M: boolean; + V3R: boolean; + VDuet1ThreeP: boolean; + VDuet2M: boolean; + VDuet3R: boolean; + V1FourP: boolean; + V2FourP: boolean; + V3FourP: boolean; + V4FourP: boolean; + V1SixP: boolean; + V2SixP: boolean; + V3SixP: boolean; + V4SixP: boolean; + V5SixP: boolean; + V6SixP: boolean; + V1DuetFourP: boolean; + V2DuetFourP: boolean; + V3DuetFourP: boolean; + V4DuetFourP: boolean; + V1DuetSixP: boolean; + V2DuetSixP: boolean; + V3DuetSixP: boolean; + V4DuetSixP: boolean; + V5DuetSixP: boolean; + V6DuetSixP: boolean; + begin + V1 := false; + V1TwoP := false; + V1ThreeP := false; + V2R := false; + V2M := false; + V3R := false; + VDuet1ThreeP := false; + VDuet2M := false; + VDuet3R := false; + V1FourP := false; + V2FourP := false; + V3FourP := false; + V4FourP := false; + V1SixP := false; + V2SixP := false; + V3SixP := false; + V4SixP := false; + V5SixP := false; + V6SixP := false; + V1DuetFourP := false; + V2DuetFourP := false; + V3DuetFourP := false; + V4DuetFourP := false; + V1DuetSixP := false; + V2DuetSixP := false; + V3DuetSixP := false; + V4DuetSixP := false; + V5DuetSixP := false; + V6DuetSixP := false; + + case PlayersPlay of + 1: + V1 := true; + 2: + begin + V1TwoP := true; + V2R := true; + end; + 3: + begin + if (CurrentSong.isDuet) then + begin + VDuet1ThreeP := true; + VDuet2M := true; + VDuet3R := true; + end + else + begin + V1ThreeP := true; + V2M := true; + V3R := true; + end; + end; + 4: + begin + if (Ini.Screens = 1) then + begin + V1TwoP := true; + V2R := true; + end + else if (CurrentSong.isDuet) then + begin + V1DuetFourP := true; + V2DuetFourP := true; + V3DuetFourP := true; + V4DuetFourP := true; + end + else + begin + V1FourP := true; + V2FourP := true; + V3FourP := true; + V4FourP := true; + end; + end; + 6: + begin + if (Ini.Screens = 1) then + begin + if (CurrentSong.isDuet) then + begin + VDuet1ThreeP := true; + VDuet2M := true; + VDuet3R := true; + end + else + begin + V1ThreeP := true; + V2M := true; + V3R := true; + end; + end + else if (CurrentSong.isDuet) then + begin + V1DuetSixP := true; + V2DuetSixP := true; + V3DuetSixP := true; + V4DuetSixP := true; + V5DuetSixP := true; + V6DuetSixP := true; + end + else + begin + V1SixP := true; + V2SixP := true; + V3SixP := true; + V4SixP := true; + V5SixP := true; + V6SixP := true; + end; + end; + end; + + SetPlayerNameVisible(TextP1, V1); + SetPlayerNameVisible(TextP1TwoP, V1TwoP); + SetPlayerNameVisible(TextP2R, V2R); + SetPlayerNameVisible(TextP1ThreeP, V1ThreeP); + SetPlayerNameVisible(TextP2M, V2M); + SetPlayerNameVisible(TextP3R, V3R); + SetPlayerNameVisible(TextDuetP1ThreeP, VDuet1ThreeP); + SetPlayerNameVisible(TextDuetP2M, VDuet2M); + SetPlayerNameVisible(TextDuetP3R, VDuet3R); + SetPlayerNameVisible(TextP1FourP, V1FourP); + SetPlayerNameVisible(TextP2FourP, V2FourP); + SetPlayerNameVisible(TextP3FourP, V3FourP); + SetPlayerNameVisible(TextP4FourP, V4FourP); + SetPlayerNameVisible(TextP1SixP, V1SixP); + SetPlayerNameVisible(TextP2SixP, V2SixP); + SetPlayerNameVisible(TextP3SixP, V3SixP); + SetPlayerNameVisible(TextP4SixP, V4SixP); + SetPlayerNameVisible(TextP5SixP, V5SixP); + SetPlayerNameVisible(TextP6SixP, V6SixP); + SetPlayerNameVisible(TextP1DuetFourP, V1DuetFourP); + SetPlayerNameVisible(TextP2DuetFourP, V2DuetFourP); + SetPlayerNameVisible(TextP3DuetFourP, V3DuetFourP); + SetPlayerNameVisible(TextP4DuetFourP, V4DuetFourP); + SetPlayerNameVisible(TextP1DuetSixP, V1DuetSixP); + SetPlayerNameVisible(TextP2DuetSixP, V2DuetSixP); + SetPlayerNameVisible(TextP3DuetSixP, V3DuetSixP); + SetPlayerNameVisible(TextP4DuetSixP, V4DuetSixP); + SetPlayerNameVisible(TextP5DuetSixP, V5DuetSixP); + SetPlayerNameVisible(TextP6DuetSixP, V6DuetSixP); + end; begin ScreenSing.Background.Draw; @@ -780,6 +960,13 @@ function TScreenSingView.Draw: boolean; // swap static textures to current screen ones SwapToScreen(ScreenAct); + UpdatePlayerNameVisibility; + + if (CurrentSong.isDuet) and (PlayersPlay = 4) then + begin + for I := 0 to High(ColPlayer) do + ColPlayer[I] := GetPlayerColor(Ini.PlayerColor[I]); + end; // draw background picture (if any, and if no visualizations) // when we don't check for visualizations the visualizations would @@ -1367,4 +1554,3 @@ procedure TScreenSingView.DrawInfoLyricBar(); end; end. - From 735e575cbc4ba8b320c5976b1bddd3d52e77c34a Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:33:12 +0200 Subject: [PATCH 10/11] Use generic slots in sing view --- src/screens/views/UScreenSingView.pas | 992 +++++++++----------------- 1 file changed, 329 insertions(+), 663 deletions(-) diff --git a/src/screens/views/UScreenSingView.pas b/src/screens/views/UScreenSingView.pas index 959e4c42e..2c597e67a 100644 --- a/src/screens/views/UScreenSingView.pas +++ b/src/screens/views/UScreenSingView.pas @@ -47,6 +47,7 @@ interface UAvatars, UMenu, UMusic, + UPlayerLayout, USingScores, USongs, UTexture, @@ -59,8 +60,6 @@ TScreenSingView = class public //StaticDuet: array of cardinal; - ColPlayer: array[0..3] of TRGB; - // lyrics bar fields StaticLyricsBar: integer; StaticLyricsBarDuet: integer; @@ -71,118 +70,9 @@ TScreenSingView = class TextTimeLabelText: integer; TextTimeText: integer; - StaticP1: array [0..1] of integer; - TextP1: integer; - StaticP1Avatar: array [0..1] of integer; - - // shown when game is in 2/4 player modus - StaticP1TwoP: array [0..1] of integer; - TextP1TwoP: integer; - StaticP1TwoPAvatar: array [0..1] of integer; - - // shown when game is in 3/6 player modus - StaticP1ThreeP: array [0..1] of integer; - TextP1ThreeP: integer; - StaticP1ThreePAvatar: array [0..1] of integer; - - StaticP2R: array [0..1] of integer; - TextP2R: integer; - StaticP2RAvatar: array [0..1] of integer; - - StaticP2M: array [0..1] of integer; - TextP2M: integer; - StaticP2MAvatar: array [0..1] of integer; - - StaticP3R: array [0..1] of integer; - TextP3R: integer; - StaticP3RAvatar: array [0..1] of integer; - - // 4/6 players in one screen - StaticP1FourP: integer; - StaticP2FourP: integer; - StaticP3FourP: integer; - StaticP4FourP: integer; - - StaticP1FourPAvatar: integer; - StaticP2FourPAvatar: integer; - StaticP3FourPAvatar: integer; - StaticP4FourPAvatar: integer; - - TextP1FourP: integer; - TextP2FourP: integer; - TextP3FourP: integer; - TextP4FourP: integer; - - StaticP1SixP: integer; - StaticP2SixP: integer; - StaticP3SixP: integer; - StaticP4SixP: integer; - StaticP5SixP: integer; - StaticP6SixP: integer; - - StaticP1SixPAvatar: integer; - StaticP2SixPAvatar: integer; - StaticP3SixPAvatar: integer; - StaticP4SixPAvatar: integer; - StaticP5SixPAvatar: integer; - StaticP6SixPAvatar: integer; - - TextP1SixP: integer; - TextP2SixP: integer; - TextP3SixP: integer; - TextP4SixP: integer; - TextP5SixP: integer; - TextP6SixP: integer; - - // 3/6 players duet - StaticDuetP1ThreeP: array [0..1] of integer; - TextDuetP1ThreeP: integer; - StaticDuetP1ThreePAvatar: array [0..1] of integer; - - StaticDuetP2M: array [0..1] of integer; - TextDuetP2M: integer; - StaticDuetP2MAvatar: array [0..1] of integer; - - StaticDuetP3R: array [0..1] of integer; - TextDuetP3R: integer; - StaticDuetP3RAvatar: array [0..1] of integer; - - // 4/6 players duet one screen - StaticP1DuetFourP: integer; - StaticP2DuetFourP: integer; - StaticP3DuetFourP: integer; - StaticP4DuetFourP: integer; - - StaticP1DuetFourPAvatar: integer; - StaticP2DuetFourPAvatar: integer; - StaticP3DuetFourPAvatar: integer; - StaticP4DuetFourPAvatar: integer; - - TextP1DuetFourP: integer; - TextP2DuetFourP: integer; - TextP3DuetFourP: integer; - TextP4DuetFourP: integer; - - StaticP1DuetSixP: integer; - StaticP2DuetSixP: integer; - StaticP3DuetSixP: integer; - StaticP4DuetSixP: integer; - StaticP5DuetSixP: integer; - StaticP6DuetSixP: integer; - - StaticP1DuetSixPAvatar: integer; - StaticP2DuetSixPAvatar: integer; - StaticP3DuetSixPAvatar: integer; - StaticP4DuetSixPAvatar: integer; - StaticP5DuetSixPAvatar: integer; - StaticP6DuetSixPAvatar: integer; - - TextP1DuetSixP: integer; - TextP2DuetSixP: integer; - TextP3DuetSixP: integer; - TextP4DuetSixP: integer; - TextP5DuetSixP: integer; - TextP6DuetSixP: integer; + PlayerFrameSlots: array [1..UIni.IMaxPlayerCount, 0..UIni.IMaxPlayerCount-1] of integer; + PlayerAvatarSlots: array [1..UIni.IMaxPlayerCount, 0..UIni.IMaxPlayerCount-1] of integer; + PlayerTextSlots: array [1..UIni.IMaxPlayerCount, 0..UIni.IMaxPlayerCount-1] of integer; StaticPausePopup: integer; @@ -239,202 +129,232 @@ implementation const MAX_MESSAGE = 3; +type + TSlotArray = array of integer; + +procedure GetSingWidgetSlots(const View: TScreenSingView; LayoutPlayerCount: integer; + out FrameSlots, AvatarSlots: TSlotArray); +var + SlotIndex: integer; +begin + SetLength(FrameSlots, LayoutPlayerCount); + SetLength(AvatarSlots, LayoutPlayerCount); + for SlotIndex := 0 to LayoutPlayerCount - 1 do + begin + FrameSlots[SlotIndex] := View.PlayerFrameSlots[LayoutPlayerCount, SlotIndex]; + AvatarSlots[SlotIndex] := View.PlayerAvatarSlots[LayoutPlayerCount, SlotIndex]; + end; +end; + +procedure GetSingTextSlots(const View: TScreenSingView; LayoutPlayerCount: integer; + out TextSlots: TSlotArray); +var + SlotIndex: integer; +begin + SetLength(TextSlots, LayoutPlayerCount); + for SlotIndex := 0 to LayoutPlayerCount - 1 do + TextSlots[SlotIndex] := View.PlayerTextSlots[LayoutPlayerCount, SlotIndex]; +end; + +function GetSingPlayerColor(PlayerIndex: integer): TRGB; +begin + Result := GetPlayerColor(Ini.PlayerColor[PlayerIndex]); +end; + +function IsCurrentSongDuet: boolean; +begin + Result := Assigned(CurrentSong) and CurrentSong.isDuet; +end; + +function BuildSingPlayerTemplate(const PlayerCountOnScreen, PlayerIndexOnScreen: integer): TThemeSingPlayer; +var + BaseTemplate: TThemeSingPlayer; + LaneLeft: integer; + LaneRight: integer; + LaneTop: integer; + LaneWidth: integer; + Scale: real; + FrameW: integer; + FrameH: integer; + AvatarInsetX: integer; + AvatarInsetY: integer; + ScoreW: integer; + ScoreH: integer; + NameX: integer; + NameY: integer; + NameW: integer; + GroupTop: integer; + HeaderOffsetLeft: integer; + Layout: TSingLaneLayout; +begin + BaseTemplate := Theme.Sing.PlayerTemplate; + Layout := GetSingLaneLayout(PlayerCountOnScreen, PlayerIndexOnScreen, Theme.Sing.PlayerLayout, + IsCurrentSongDuet and (PlayersPlay <> 1)); + LaneLeft := Layout.ColumnLeft; + LaneRight := Layout.ColumnRight; + LaneTop := Layout.RowAnchorY; + LaneWidth := Layout.ColumnWidth; + Scale := Layout.WidgetScale; + + FrameW := Max(Theme.Sing.PlayerWidgetLayout.MinFrameW, Round(BaseTemplate.AvatarFrame.W * Scale)); + FrameH := Max(Theme.Sing.PlayerWidgetLayout.MinFrameH, Round(BaseTemplate.AvatarFrame.H * Scale)); + ScoreW := Max(Theme.Sing.PlayerWidgetLayout.MinScoreW, Round(BaseTemplate.ScoreBackground.W * Scale)); + ScoreH := Max(Theme.Sing.PlayerWidgetLayout.MinScoreH, Round(BaseTemplate.ScoreBackground.H * Scale)); + HeaderOffsetLeft := Round(Theme.Sing.PlayerWidgetLayout.HeaderOffsetLeft * Scale); + GroupTop := Max(10, LaneTop - + GetSingHeaderTopOffset(Theme.Sing.PlayerWidgetLayout, Layout.GridRows, Scale)); + AvatarInsetX := Max(Theme.Sing.PlayerWidgetLayout.MinAvatarInsetX, + Round((BaseTemplate.Avatar.X - BaseTemplate.AvatarFrame.X) * Scale)); + AvatarInsetY := Max(Theme.Sing.PlayerWidgetLayout.MinAvatarInsetY, + Round((BaseTemplate.Avatar.Y - BaseTemplate.AvatarFrame.Y) * Scale)); + NameX := Max(0, LaneLeft - HeaderOffsetLeft) + FrameW + + Max(Theme.Sing.PlayerWidgetLayout.NameGapMinX, Round(Theme.Sing.PlayerWidgetLayout.NameGapBaseX * Scale)); + NameW := Max(Theme.Sing.PlayerWidgetLayout.NameMinW, + (LaneRight - ScoreW - Max(Theme.Sing.PlayerWidgetLayout.NameGapMinX, + Round(Theme.Sing.PlayerWidgetLayout.NameGapBaseX * Scale))) - NameX); + NameY := GroupTop + Max(0, (FrameH - Max(12, Round(BaseTemplate.Name.Size * Scale))) div 2); + + Result := BaseTemplate; + Result.AvatarFrame.X := Max(0, LaneLeft - HeaderOffsetLeft); + Result.AvatarFrame.Y := GroupTop; + Result.AvatarFrame.W := FrameW; + Result.AvatarFrame.H := FrameH; + + Result.Avatar.X := Result.AvatarFrame.X + AvatarInsetX; + Result.Avatar.Y := Result.AvatarFrame.Y + AvatarInsetY; + Result.Avatar.W := Max(1, FrameW - 2 * AvatarInsetX); + Result.Avatar.H := Max(1, FrameH - 2 * AvatarInsetY); + + Result.Name.X := Max(0, NameX - Max(Theme.Sing.PlayerWidgetLayout.NamePaddingMinX, + Round(Theme.Sing.PlayerWidgetLayout.NamePaddingBaseX * Scale))); + Result.Name.Y := Max(0, NameY - Max(Theme.Sing.PlayerWidgetLayout.NamePaddingMinY, + Round(Theme.Sing.PlayerWidgetLayout.NamePaddingBaseY * Scale))); + Result.Name.W := NameW; + Result.Name.H := Max(Theme.Sing.PlayerWidgetLayout.NameMinH, Round(BaseTemplate.Name.H * Scale)); + Result.Name.Size := Max(Theme.Sing.PlayerWidgetLayout.NameMinSize, Round(BaseTemplate.Name.Size * Scale)); + if LaneWidth <= 0 then + Result.Name.W := 0; +end; + +procedure ApplySingPlayerTemplate(const SingPlayer: TThemeSingPlayer; FrameSlot, AvatarSlot, TextSlot: integer); +begin + ScreenSing.Statics[FrameSlot].Texture.X := SingPlayer.AvatarFrame.X; + ScreenSing.Statics[FrameSlot].Texture.Y := SingPlayer.AvatarFrame.Y; + ScreenSing.Statics[FrameSlot].Texture.W := SingPlayer.AvatarFrame.W; + ScreenSing.Statics[FrameSlot].Texture.H := SingPlayer.AvatarFrame.H; + ScreenSing.Statics[FrameSlot].Texture.Z := SingPlayer.AvatarFrame.Z; + ScreenSing.Statics[FrameSlot].Texture.Alpha := SingPlayer.AvatarFrame.Alpha; + + ScreenSing.Statics[AvatarSlot].Texture.X := SingPlayer.Avatar.X; + ScreenSing.Statics[AvatarSlot].Texture.Y := SingPlayer.Avatar.Y; + ScreenSing.Statics[AvatarSlot].Texture.W := SingPlayer.Avatar.W; + ScreenSing.Statics[AvatarSlot].Texture.H := SingPlayer.Avatar.H; + ScreenSing.Statics[AvatarSlot].Texture.Z := SingPlayer.Avatar.Z; + ScreenSing.Statics[AvatarSlot].Texture.Alpha := SingPlayer.Avatar.Alpha; + + ScreenSing.Text[TextSlot].X := SingPlayer.Name.X; + ScreenSing.Text[TextSlot].Y := SingPlayer.Name.Y; + ScreenSing.Text[TextSlot].W := SingPlayer.Name.W; + ScreenSing.Text[TextSlot].H := SingPlayer.Name.H; + ScreenSing.Text[TextSlot].Z := SingPlayer.Name.Z; + ScreenSing.Text[TextSlot].Size := SingPlayer.Name.Size; +end; + //ToDo basisbit: check this again // Dirty HacK procedure TScreenSingView.SwapToScreen(Screen: integer); - procedure setVisible(elements: array of integer; visible: boolean); +var + LocalPlayerCount: integer; + FirstPlayerIndex: integer; + FrameSlots: TSlotArray; + AvatarSlots: TSlotArray; + IterLayoutPlayerCount: integer; + procedure setVisible(const elements: TSlotArray; visible: boolean); var J: integer; begin for J := 0 to High(elements) do ScreenSing.Statics[elements[J]].Visible := visible; end; - procedure hide(elements: array of integer); + procedure hide(const elements: TSlotArray); begin setVisible(elements, false); end; - procedure maybeShow(elements: array of integer); + procedure maybeShowCount(const elements: TSlotArray; Count: integer); + var + J: integer; begin - setVisible(elements, ScreenSing.Settings.AvatarsVisible); + if not ScreenSing.Settings.AvatarsVisible then + Exit; + + for J := 0 to Count - 1 do + ScreenSing.Statics[elements[J]].Visible := true; end; -begin - { if screens = 2 and playerplay <= 3 the 2nd screen shows the - textures of screen 1 } - if (PlayersPlay <= 3) and (Screen = 2) then - Screen := 1; - - hide([ - // 1P - screen 1 - StaticP1[0], StaticP1Avatar[0], - // 2P - screen 1 - StaticP1TwoP[0], StaticP1TwoPAvatar[0], - StaticP2R[0], StaticP2RAvatar[0], - // 3P - screen 1 - StaticP1ThreeP[0], StaticP1ThreePAvatar[0], - StaticP2M[0], StaticP2MAvatar[0], - StaticP3R[0], StaticP3RAvatar[0], - // 1P - screen 2 (2 players across 2 screens) - StaticP1[1], StaticP1Avatar[1], - // 2P - screen 2 (4 players across 2 screens) - StaticP1TwoP[1], StaticP1TwoPAvatar[1], - StaticP2R[1], StaticP2RAvatar[1], - // 3P - screen 2 (6 players across 2 screens) - StaticP1ThreeP[1], StaticP1ThreePAvatar[1], - StaticP2M[1], StaticP2MAvatar[1], - StaticP3R[1], StaticP3RAvatar[1], - // 3P duet - screen 1 - StaticDuetP1ThreeP[0], StaticDuetP1ThreePAvatar[0], - StaticDuetP2M[0], StaticDuetP2MAvatar[0], - StaticDuetP3R[0], StaticDuetP3RAvatar[0], - // 3P duet - screen 2 (6 players across 2 screens) - StaticDuetP1ThreeP[1], StaticDuetP1ThreePAvatar[1], - StaticDuetP2M[1], StaticDuetP2MAvatar[1], - StaticDuetP3R[1], StaticDuetP3RAvatar[1], - // 4P - screen 1 - StaticP1FourP, StaticP1FourPAvatar, - StaticP2FourP, StaticP2FourPAvatar, - StaticP3FourP, StaticP3FourPAvatar, - StaticP4FourP, StaticP4FourPAvatar, - // 4P duet - screen 1 - StaticP1DuetFourP, StaticP1DuetFourPAvatar, - StaticP2DuetFourP, StaticP2DuetFourPAvatar, - StaticP3DuetFourP, StaticP3DuetFourPAvatar, - StaticP4DuetFourP, StaticP4DuetFourPAvatar, - // 6P - screen 1 - StaticP1SixP, StaticP1SixPAvatar, - StaticP2SixP, StaticP2SixPAvatar, - StaticP3SixP, StaticP3SixPAvatar, - StaticP4SixP, StaticP4SixPAvatar, - StaticP5SixP, StaticP5SixPAvatar, - StaticP6SixP, StaticP6SixPAvatar, - // 6P duet - screen 1 - StaticP1DuetSixP, StaticP1DuetSixPAvatar, - StaticP2DuetSixP, StaticP2DuetSixPAvatar, - StaticP3DuetSixP, StaticP3DuetSixPAvatar, - StaticP4DuetSixP, StaticP4DuetSixPAvatar, - StaticP5DuetSixP, StaticP5DuetSixPAvatar, - StaticP6DuetSixP, StaticP6DuetSixPAvatar - ]); - - if (PlayersPlay = 1) then + procedure hideGroup(APlayerCount: integer); begin - // this is actually correct! it just puts the sole player on all screens - maybeShow([StaticP1[0], StaticP1Avatar[0]]); + GetSingWidgetSlots(Self, APlayerCount, FrameSlots, AvatarSlots); + hide(FrameSlots); + hide(AvatarSlots); end; - - if (PlayersPlay = 2) or ((PlayersPlay = 4) and (Ini.Screens = 1)) then + procedure setStaticColor(StaticIndex, PlayerIndex: integer); + var + PlayerColor: TRGB; begin - if (Screen = 2) then - begin - maybeShow([ - StaticP1TwoP[1], StaticP1TwoPAvatar[1], - StaticP2R[1], StaticP2RAvatar[1] - ]); - end; - - if (Screen = 1) then - begin - maybeShow([ - StaticP1TwoP[0], StaticP1TwoPAvatar[0], - StaticP2R[0], StaticP2RAvatar[0] - ]); - end; + PlayerColor := GetPlayerColor(Ini.SingColor[PlayerIndex]); + ScreenSing.Statics[StaticIndex].Texture.ColR := PlayerColor.R; + ScreenSing.Statics[StaticIndex].Texture.ColG := PlayerColor.G; + ScreenSing.Statics[StaticIndex].Texture.ColB := PlayerColor.B; end; - - if (PlayersPlay = 3) or ((PlayersPlay = 6) and (Ini.Screens = 1)) then + procedure setVisibleSlotColors(const Slots: TSlotArray; Count, FirstIndex: integer); + var + J: integer; begin - if (CurrentSong.isDuet) then - begin - if (Screen = 2) then - begin - maybeShow([ - StaticDuetP1ThreeP[1], StaticDuetP1ThreePAvatar[1], - StaticDuetP2M[1], StaticDuetP2MAvatar[1], - StaticDuetP3R[1], StaticDuetP3RAvatar[1] - ]); - end; - - if (Screen = 1) then - begin - maybeShow([ - StaticDuetP1ThreeP[0], StaticDuetP1ThreePAvatar[0], - StaticDuetP2M[0], StaticDuetP2MAvatar[0], - StaticDuetP3R[0], StaticDuetP3RAvatar[0] - ]); - end; - end - else + for J := 0 to Count - 1 do + setStaticColor(Slots[J], FirstIndex + J); + end; + procedure setVisibleSlotAvatars(const Slots: TSlotArray; Count, FirstIndex: integer); + var + J: integer; + PlayerIndex: integer; + CurrentTexture: TTexture; + begin + for J := 0 to Count - 1 do begin - if (Screen = 2) then + PlayerIndex := FirstIndex + J + 1; + if (PlayerIndex >= 1) and (PlayerIndex <= UIni.IMaxPlayerCount) then begin - maybeShow([ - StaticP1ThreeP[1], StaticP1ThreePAvatar[1], - StaticP2M[1], StaticP2MAvatar[1], - StaticP3R[1], StaticP3RAvatar[1] - ]); - end; - - if (Screen = 1) then - begin - maybeShow([ - StaticP1ThreeP[0], StaticP1ThreePAvatar[0], - StaticP2M[0], StaticP2MAvatar[0], - StaticP3R[0], StaticP3RAvatar[0] - ]); + CurrentTexture := ScreenSing.Statics[Slots[J]].Texture; + ScreenSing.Statics[Slots[J]].Texture := AvatarPlayerTextures[PlayerIndex]; + ScreenSing.Statics[Slots[J]].Texture.X := CurrentTexture.X; + ScreenSing.Statics[Slots[J]].Texture.Y := CurrentTexture.Y; + ScreenSing.Statics[Slots[J]].Texture.W := CurrentTexture.W; + ScreenSing.Statics[Slots[J]].Texture.H := CurrentTexture.H; + ScreenSing.Statics[Slots[J]].Texture.Z := CurrentTexture.Z; + ScreenSing.Statics[Slots[J]].Texture.Alpha := CurrentTexture.Alpha; end; end; end; +begin + for IterLayoutPlayerCount := 1 to UIni.IMaxPlayerCount do + hideGroup(IterLayoutPlayerCount); - // 4 Players in 1 Screen - if (PlayersPlay = 4) and (Ini.Screens = 0) then + if Ini.Screens = 1 then begin - if (CurrentSong.isDuet) then - begin - maybeShow([ - StaticP1DuetFourP, StaticP1DuetFourPAvatar, - StaticP2DuetFourP, StaticP2DuetFourPAvatar, - StaticP3DuetFourP, StaticP3DuetFourPAvatar, - StaticP4DuetFourP, StaticP4DuetFourPAvatar - ]); - end - else - begin - maybeShow([ - StaticP1FourP, StaticP1FourPAvatar, - StaticP2FourP, StaticP2FourPAvatar, - StaticP3FourP, StaticP3FourPAvatar, - StaticP4FourP, StaticP4FourPAvatar - ]); - end; - end; - - // 6 Players in 1 Screen - if (PlayersPlay = 6) and (Ini.Screens = 0) then + LocalPlayerCount := GetScreenPlayerCount(PlayersPlay, 2, Screen); + FirstPlayerIndex := GetFirstPlayerIndexForScreen(PlayersPlay, 2, Screen); + end + else begin - if (CurrentSong.isDuet) then - begin - maybeShow([ - StaticP1DuetSixP, StaticP1DuetSixPAvatar, - StaticP2DuetSixP, StaticP2DuetSixPAvatar, - StaticP3DuetSixP, StaticP3DuetSixPAvatar, - StaticP4DuetSixP, StaticP4DuetSixPAvatar, - StaticP5DuetSixP, StaticP5DuetSixPAvatar, - StaticP6DuetSixP, StaticP6DuetSixPAvatar - ]); - end - else - begin - maybeShow([ - StaticP1SixP, StaticP1SixPAvatar, - StaticP2SixP, StaticP2SixPAvatar, - StaticP3SixP, StaticP3SixPAvatar, - StaticP4SixP, StaticP4SixPAvatar, - StaticP5SixP, StaticP5SixPAvatar, - StaticP6SixP, StaticP6SixPAvatar - ]); - end; + LocalPlayerCount := PlayersPlay; + FirstPlayerIndex := 0; end; + GetSingWidgetSlots(Self, LocalPlayerCount, FrameSlots, AvatarSlots); + setVisibleSlotColors(FrameSlots, LocalPlayerCount, FirstPlayerIndex); + setVisibleSlotAvatars(AvatarSlots, LocalPlayerCount, FirstPlayerIndex); + maybeShowCount(FrameSlots, LocalPlayerCount); + maybeShowCount(AvatarSlots, LocalPlayerCount); + end; constructor TScreenSingView.Create; @@ -454,12 +374,26 @@ constructor TScreenSingView.Create; setColor(avatarFrame, color); static := ScreenSing.AddStatic(avatarFrame); end; + procedure setColorAndAssignAvatarFrameStaticForSlot(const PlayerCountOnScreen, PlayerIndexOnScreen: integer; var static: integer; color: TRGB); + var + SingPlayer: TThemeSingPlayer; + begin + SingPlayer := BuildSingPlayerTemplate(PlayerCountOnScreen, PlayerIndexOnScreen); + setColorAndAssignAvatarFrameStatic(SingPlayer.AvatarFrame, static, color); + end; // passing the integers for the statics by reference is deliberate procedure setColorAndAssignStatics(var singPlayer: TThemeSingPlayer; var avatarFrameStatic: integer; var nameStatic: integer; color: TRGB); begin setColorAndAssignAvatarFrameStatic(singPlayer.AvatarFrame, avatarFrameStatic, color); nameStatic := ScreenSing.AddText(singPlayer.Name); end; + procedure setColorAndAssignStaticsForSlot(const PlayerCountOnScreen, PlayerIndexOnScreen: integer; var avatarFrameStatic: integer; var nameStatic: integer; color: TRGB); + var + SingPlayer: TThemeSingPlayer; + begin + SingPlayer := BuildSingPlayerTemplate(PlayerCountOnScreen, PlayerIndexOnScreen); + setColorAndAssignStatics(SingPlayer, avatarFrameStatic, nameStatic, color); + end; // passing the integer for the static by reference is deliberate procedure assignAvatarStatic(var singPlayer: TThemeSingPlayer; var avatarStatic: integer; var texture: TTexture); begin @@ -472,6 +406,53 @@ constructor TScreenSingView.Create; ScreenSing.Statics[avatarStatic].Texture.Z := singPlayer.Avatar.Z; ScreenSing.Statics[avatarStatic].Texture.Alpha := singPlayer.Avatar.Alpha; end; + procedure assignAvatarStaticForSlot(const PlayerCountOnScreen, PlayerIndexOnScreen: integer; var avatarStatic: integer; var texture: TTexture); + var + SingPlayer: TThemeSingPlayer; + begin + SingPlayer := BuildSingPlayerTemplate(PlayerCountOnScreen, PlayerIndexOnScreen); + assignAvatarStatic(SingPlayer, avatarStatic, texture); + end; + procedure InitFrameSlots(MinLayoutPlayerCount, MaxLayoutPlayerCount: integer; CreateTextSlots: boolean); + var + LayoutPlayerCount: integer; + SlotIndex: integer; + ColorIndex: integer; + begin + for LayoutPlayerCount := MinLayoutPlayerCount to MaxLayoutPlayerCount do + begin + for SlotIndex := 0 to LayoutPlayerCount - 1 do + begin + ColorIndex := (SlotIndex mod UIni.IMaxPlayerCount) + 1; + if CreateTextSlots then + setColorAndAssignStaticsForSlot(LayoutPlayerCount, SlotIndex, + PlayerFrameSlots[LayoutPlayerCount, SlotIndex], + PlayerTextSlots[LayoutPlayerCount, SlotIndex], + Col[ColorIndex]) + else + setColorAndAssignAvatarFrameStaticForSlot(LayoutPlayerCount, SlotIndex, + PlayerFrameSlots[LayoutPlayerCount, SlotIndex], + Col[ColorIndex]); + end; + end; + end; + procedure InitAvatarSlots(MaxLayoutPlayerCount: integer); + var + LayoutPlayerCount: integer; + SlotIndex: integer; + PlayerIndex: integer; + begin + for LayoutPlayerCount := 1 to MaxLayoutPlayerCount do + begin + for SlotIndex := 0 to LayoutPlayerCount - 1 do + begin + PlayerIndex := (SlotIndex mod UIni.IMaxPlayerCount) + 1; + assignAvatarStaticForSlot(LayoutPlayerCount, SlotIndex, + PlayerAvatarSlots[LayoutPlayerCount, SlotIndex], + AvatarPlayerTextures[PlayerIndex]); + end; + end; + end; begin lastVolume:= -1; //too dangerous, a mouse button is quickly pressed by accident @@ -506,41 +487,7 @@ constructor TScreenSingView.Create; for I := 1 to UIni.IMaxPlayerCount do Col[I] := GetPlayerColor(Ini.SingColor[I - 1]); - // SCREEN 1 - // 1 player - setColorAndAssignAvatarFrameStatic(Theme.Sing.Solo1PP1.AvatarFrame, StaticP1[0], Col[1]); - - // 2 or 4 players - setColorAndAssignAvatarFrameStatic(Theme.Sing.Solo2PP1.AvatarFrame, StaticP1TwoP[0], Col[1]); - setColorAndAssignAvatarFrameStatic(Theme.Sing.Solo2PP2.AvatarFrame, StaticP2R[0] , Col[2]); - - // 3 or 6 players - setColorAndAssignAvatarFrameStatic(Theme.Sing.Solo3PP1.AvatarFrame, StaticP1ThreeP[0], Col[1]); - setColorAndAssignAvatarFrameStatic(Theme.Sing.Solo3PP2.AvatarFrame, StaticP2M[0] , Col[2]); - setColorAndAssignAvatarFrameStatic(Theme.Sing.Solo3PP3.AvatarFrame, StaticP3R[0] , Col[3]); - - // 3 or 6 players duet - setColorAndAssignAvatarFrameStatic(Theme.Sing.Duet3PP1.AvatarFrame, StaticDuetP1ThreeP[0], Col[1]); - setColorAndAssignAvatarFrameStatic(Theme.Sing.Duet3PP2.AvatarFrame, StaticDuetP2M[0] , Col[2]); - setColorAndAssignAvatarFrameStatic(Theme.Sing.Duet3PP3.AvatarFrame, StaticDuetP3R[0] , Col[3]); - - // SCREEN 2 - // 1 player - setColorAndAssignStatics(Theme.Sing.Solo1PP1, StaticP1[1], TextP1, Col[1]); - - // 2 or 4 players - setColorAndAssignStatics(Theme.Sing.Solo2PP1, StaticP1TwoP[1], TextP1TwoP, Col[3]); - setColorAndAssignStatics(Theme.Sing.Solo2PP2, StaticP2R[1] , TextP2R , Col[4]); - - // 3 or 6 players - setColorAndAssignStatics(Theme.Sing.Solo3PP1, StaticP1ThreeP[1], TextP1ThreeP, Col[4]); - setColorAndAssignStatics(Theme.Sing.Solo3PP2, StaticP2M[1] , TextP2M , Col[5]); - setColorAndAssignStatics(Theme.Sing.Solo3PP3, StaticP3R[1] , TextP3R , Col[6]); - - // 3 or 6 players duet - setColorAndAssignStatics(Theme.Sing.Duet3PP1, StaticDuetP1ThreeP[1], TextDuetP1ThreeP, Col[4]); - setColorAndAssignStatics(Theme.Sing.Duet3PP2, StaticDuetP2M[1] , TextDuetP2M , Col[5]); - setColorAndAssignStatics(Theme.Sing.Duet3PP3, StaticDuetP3R[1] , TextDuetP3R , Col[6]); + InitFrameSlots(1, UIni.IMaxPlayerCount, true); for I := 1 to PlayersPlay do begin @@ -555,35 +502,6 @@ constructor TScreenSingView.Create; ScreenSing.PlayerDuetNames[I] := ScreenSing.PlayerNames[I]; end; - // 4 players in 1 screen - setColorAndAssignStatics(Theme.Sing.Solo4PP1, StaticP1FourP, TextP1FourP, Col[1]); - setColorAndAssignStatics(Theme.Sing.Solo4PP2, StaticP2FourP, TextP2FourP, Col[2]); - setColorAndAssignStatics(Theme.Sing.Solo4PP3, StaticP3FourP, TextP3FourP, Col[3]); - setColorAndAssignStatics(Theme.Sing.Solo4PP4, StaticP4FourP, TextP4FourP, Col[4]); - - // 6 players in 1 screen - setColorAndAssignStatics(Theme.Sing.Solo6PP1, StaticP1SixP, TextP1SixP, Col[1]); - setColorAndAssignStatics(Theme.Sing.Solo6PP2, StaticP2SixP, TextP2SixP, Col[2]); - setColorAndAssignStatics(Theme.Sing.Solo6PP3, StaticP3SixP, TextP3SixP, Col[3]); - setColorAndAssignStatics(Theme.Sing.Solo6PP4, StaticP4SixP, TextP4SixP, Col[4]); - setColorAndAssignStatics(Theme.Sing.Solo6PP5, StaticP5SixP, TextP5SixP, Col[5]); - setColorAndAssignStatics(Theme.Sing.Solo6PP6, StaticP6SixP, TextP6SixP, Col[6]); - - - // 4 players duet in 1 screen - setColorAndAssignStatics(Theme.Sing.Duet4PP1, StaticP1DuetFourP, TextP1DuetFourP, Col[1]); - setColorAndAssignStatics(Theme.Sing.Duet4PP2, StaticP2DuetFourP, TextP2DuetFourP, Col[2]); - setColorAndAssignStatics(Theme.Sing.Duet4PP3, StaticP3DuetFourP, TextP3DuetFourP, Col[3]); - setColorAndAssignStatics(Theme.Sing.Duet4PP4, StaticP4DuetFourP, TextP4DuetFourP, Col[4]); - - // 6 players duet in 1 screen - setColorAndAssignStatics(Theme.Sing.Duet6PP1, StaticP1DuetSixP, TextP1DuetSixP, Col[1]); - setColorAndAssignStatics(Theme.Sing.Duet6PP2, StaticP2DuetSixP, TextP2DuetSixP, Col[2]); - setColorAndAssignStatics(Theme.Sing.Duet6PP3, StaticP3DuetSixP, TextP3DuetSixP, Col[3]); - setColorAndAssignStatics(Theme.Sing.Duet6PP4, StaticP4DuetSixP, TextP4DuetSixP, Col[4]); - setColorAndAssignStatics(Theme.Sing.Duet6PP5, StaticP5DuetSixP, TextP5DuetSixP, Col[5]); - setColorAndAssignStatics(Theme.Sing.Duet6PP6, StaticP6DuetSixP, TextP6DuetSixP, Col[6]); - // Sing Bars // P1-6 for I := 1 to UIni.IMaxPlayerCount do @@ -659,60 +577,7 @@ constructor TScreenSingView.Create; ScreenSing.InfoMessageBG := ScreenSing.AddStatic(Theme.Sing.InfoMessageBG); ScreenSing.InfoMessageText := ScreenSing.AddText(Theme.Sing.InfoMessageText); - // avatars - // SCREEN 1 - // 1P - assignAvatarStatic(Theme.Sing.Solo1PP1, StaticP1Avatar[0], AvatarPlayerTextures[1]); - // 2P - assignAvatarStatic(Theme.Sing.Solo2PP1, StaticP1TwoPAvatar[0], AvatarPlayerTextures[1]); - assignAvatarStatic(Theme.Sing.Solo2PP2, StaticP2RAvatar[0] , AvatarPlayerTextures[2]); - // 3P - assignAvatarStatic(Theme.Sing.Solo3PP1, StaticP1ThreePAvatar[0], AvatarPlayerTextures[1]); - assignAvatarStatic(Theme.Sing.Solo3PP2, StaticP2MAvatar[0] , AvatarPlayerTextures[2]); - assignAvatarStatic(Theme.Sing.Solo3PP3, StaticP3RAvatar[0] , AvatarPlayerTextures[3]); - // 3P duet - assignAvatarStatic(Theme.Sing.Duet3PP1, StaticDuetP1ThreePAvatar[0], AvatarPlayerTextures[1]); - assignAvatarStatic(Theme.Sing.Duet3PP2, StaticDuetP2MAvatar[0] , AvatarPlayerTextures[2]); - assignAvatarStatic(Theme.Sing.Duet3PP3, StaticDuetP3RAvatar[0] , AvatarPlayerTextures[3]); - // 4P - assignAvatarStatic(Theme.Sing.Solo4PP1, StaticP1FourPAvatar, AvatarPlayerTextures[1]); - assignAvatarStatic(Theme.Sing.Solo4PP2, StaticP2FourPAvatar, AvatarPlayerTextures[2]); - assignAvatarStatic(Theme.Sing.Solo4PP3, StaticP3FourPAvatar, AvatarPlayerTextures[3]); - assignAvatarStatic(Theme.Sing.Solo4PP4, StaticP4FourPAvatar, AvatarPlayerTextures[4]); - // 4P duet - assignAvatarStatic(Theme.Sing.Duet4PP1, StaticP1DuetFourPAvatar, AvatarPlayerTextures[1]); - assignAvatarStatic(Theme.Sing.Duet4PP2, StaticP2DuetFourPAvatar, AvatarPlayerTextures[2]); - assignAvatarStatic(Theme.Sing.Duet4PP3, StaticP3DuetFourPAvatar, AvatarPlayerTextures[3]); - assignAvatarStatic(Theme.Sing.Duet4PP4, StaticP4DuetFourPAvatar, AvatarPlayerTextures[4]); - // 6P - assignAvatarStatic(Theme.Sing.Solo6PP1, StaticP1SixPAvatar, AvatarPlayerTextures[1]); - assignAvatarStatic(Theme.Sing.Solo6PP2, StaticP2SixPAvatar, AvatarPlayerTextures[2]); - assignAvatarStatic(Theme.Sing.Solo6PP3, StaticP3SixPAvatar, AvatarPlayerTextures[3]); - assignAvatarStatic(Theme.Sing.Solo6PP4, StaticP4SixPAvatar, AvatarPlayerTextures[4]); - assignAvatarStatic(Theme.Sing.Solo6PP5, StaticP5SixPAvatar, AvatarPlayerTextures[5]); - assignAvatarStatic(Theme.Sing.Solo6PP6, StaticP6SixPAvatar, AvatarPlayerTextures[6]); - // 6P duet - assignAvatarStatic(Theme.Sing.Duet6PP1, StaticP1DuetSixPAvatar, AvatarPlayerTextures[1]); - assignAvatarStatic(Theme.Sing.Duet6PP2, StaticP2DuetSixPAvatar, AvatarPlayerTextures[2]); - assignAvatarStatic(Theme.Sing.Duet6PP3, StaticP3DuetSixPAvatar, AvatarPlayerTextures[3]); - assignAvatarStatic(Theme.Sing.Duet6PP4, StaticP4DuetSixPAvatar, AvatarPlayerTextures[4]); - assignAvatarStatic(Theme.Sing.Duet6PP5, StaticP5DuetSixPAvatar, AvatarPlayerTextures[5]); - assignAvatarStatic(Theme.Sing.Duet6PP6, StaticP6DuetSixPAvatar, AvatarPlayerTextures[6]); - - // SCREEN 2 - // 1P - assignAvatarStatic(Theme.Sing.Solo1PP1, StaticP1Avatar[1], AvatarPlayerTextures[2]); - // 2P - assignAvatarStatic(Theme.Sing.Solo2PP1, StaticP1TwoPAvatar[1], AvatarPlayerTextures[3]); - assignAvatarStatic(Theme.Sing.Solo2PP2, StaticP2RAvatar[1] , AvatarPlayerTextures[4]); - // 3P - assignAvatarStatic(Theme.Sing.Solo3PP1, StaticP1ThreePAvatar[1], AvatarPlayerTextures[4]); - assignAvatarStatic(Theme.Sing.Solo3PP2, StaticP2MAvatar[1] , AvatarPlayerTextures[5]); - assignAvatarStatic(Theme.Sing.Solo3PP3, StaticP3RAvatar[1] , AvatarPlayerTextures[6]); - // 3P duet - assignAvatarStatic(Theme.Sing.Duet3PP1, StaticDuetP1ThreePAvatar[1], AvatarPlayerTextures[4]); - assignAvatarStatic(Theme.Sing.Duet3PP2, StaticDuetP2MAvatar[1] , AvatarPlayerTextures[5]); - assignAvatarStatic(Theme.Sing.Duet3PP3, StaticDuetP3RAvatar[1] , AvatarPlayerTextures[6]); + InitAvatarSlots(UIni.IMaxPlayerCount); end; destructor TScreenSingView.Destroy; @@ -756,7 +621,6 @@ function TScreenSingView.Draw: boolean; DisplayPrefix: string; DisplayMin: integer; DisplaySec: integer; - I: integer; T: integer; CurLyricsTime: real; VideoFrameTime: Extended; @@ -764,184 +628,53 @@ function TScreenSingView.Draw: boolean; medley_end: boolean; medley_start_applause: boolean; LastLineSungToEnd: boolean; - procedure SetPlayerNameVisible(TextIndex: integer; Visible: boolean); - begin - ScreenSing.Text[TextIndex].Visible := Visible and ScreenSing.Settings.AvatarsVisible; - end; - procedure UpdatePlayerNameVisibility; + LocalPlayerCount: integer; + LocalStartIndex: integer; + TextSlots: TSlotArray; + IterLayoutPlayerCount: integer; + SlotIndex: integer; + SingPlayer: TThemeSingPlayer; + procedure SetPlayerNameTexts(const Slots: TSlotArray; FirstPlayerIndex: integer; UseDuetNames: boolean); var - V1: boolean; - V1TwoP: boolean; - V1ThreeP: boolean; - V2R: boolean; - V2M: boolean; - V3R: boolean; - VDuet1ThreeP: boolean; - VDuet2M: boolean; - VDuet3R: boolean; - V1FourP: boolean; - V2FourP: boolean; - V3FourP: boolean; - V4FourP: boolean; - V1SixP: boolean; - V2SixP: boolean; - V3SixP: boolean; - V4SixP: boolean; - V5SixP: boolean; - V6SixP: boolean; - V1DuetFourP: boolean; - V2DuetFourP: boolean; - V3DuetFourP: boolean; - V4DuetFourP: boolean; - V1DuetSixP: boolean; - V2DuetSixP: boolean; - V3DuetSixP: boolean; - V4DuetSixP: boolean; - V5DuetSixP: boolean; - V6DuetSixP: boolean; + J: integer; + PlayerIndex: integer; begin - V1 := false; - V1TwoP := false; - V1ThreeP := false; - V2R := false; - V2M := false; - V3R := false; - VDuet1ThreeP := false; - VDuet2M := false; - VDuet3R := false; - V1FourP := false; - V2FourP := false; - V3FourP := false; - V4FourP := false; - V1SixP := false; - V2SixP := false; - V3SixP := false; - V4SixP := false; - V5SixP := false; - V6SixP := false; - V1DuetFourP := false; - V2DuetFourP := false; - V3DuetFourP := false; - V4DuetFourP := false; - V1DuetSixP := false; - V2DuetSixP := false; - V3DuetSixP := false; - V4DuetSixP := false; - V5DuetSixP := false; - V6DuetSixP := false; - - case PlayersPlay of - 1: - V1 := true; - 2: - begin - V1TwoP := true; - V2R := true; - end; - 3: - begin - if (CurrentSong.isDuet) then - begin - VDuet1ThreeP := true; - VDuet2M := true; - VDuet3R := true; - end - else - begin - V1ThreeP := true; - V2M := true; - V3R := true; - end; - end; - 4: + for J := 0 to High(Slots) do + begin + PlayerIndex := FirstPlayerIndex + J; + if (PlayerIndex >= 1) and (PlayerIndex <= PlayersPlay) then begin - if (Ini.Screens = 1) then - begin - V1TwoP := true; - V2R := true; - end - else if (CurrentSong.isDuet) then - begin - V1DuetFourP := true; - V2DuetFourP := true; - V3DuetFourP := true; - V4DuetFourP := true; - end + if UseDuetNames then + ScreenSing.Text[Slots[J]].Text := ScreenSing.PlayerDuetNames[PlayerIndex] else - begin - V1FourP := true; - V2FourP := true; - V3FourP := true; - V4FourP := true; - end; - end; - 6: + ScreenSing.Text[Slots[J]].Text := ScreenSing.PlayerNames[PlayerIndex]; + end + else begin - if (Ini.Screens = 1) then - begin - if (CurrentSong.isDuet) then - begin - VDuet1ThreeP := true; - VDuet2M := true; - VDuet3R := true; - end - else - begin - V1ThreeP := true; - V2M := true; - V3R := true; - end; - end - else if (CurrentSong.isDuet) then - begin - V1DuetSixP := true; - V2DuetSixP := true; - V3DuetSixP := true; - V4DuetSixP := true; - V5DuetSixP := true; - V6DuetSixP := true; - end - else - begin - V1SixP := true; - V2SixP := true; - V3SixP := true; - V4SixP := true; - V5SixP := true; - V6SixP := true; - end; + ScreenSing.Text[Slots[J]].Text := ''; end; end; - - SetPlayerNameVisible(TextP1, V1); - SetPlayerNameVisible(TextP1TwoP, V1TwoP); - SetPlayerNameVisible(TextP2R, V2R); - SetPlayerNameVisible(TextP1ThreeP, V1ThreeP); - SetPlayerNameVisible(TextP2M, V2M); - SetPlayerNameVisible(TextP3R, V3R); - SetPlayerNameVisible(TextDuetP1ThreeP, VDuet1ThreeP); - SetPlayerNameVisible(TextDuetP2M, VDuet2M); - SetPlayerNameVisible(TextDuetP3R, VDuet3R); - SetPlayerNameVisible(TextP1FourP, V1FourP); - SetPlayerNameVisible(TextP2FourP, V2FourP); - SetPlayerNameVisible(TextP3FourP, V3FourP); - SetPlayerNameVisible(TextP4FourP, V4FourP); - SetPlayerNameVisible(TextP1SixP, V1SixP); - SetPlayerNameVisible(TextP2SixP, V2SixP); - SetPlayerNameVisible(TextP3SixP, V3SixP); - SetPlayerNameVisible(TextP4SixP, V4SixP); - SetPlayerNameVisible(TextP5SixP, V5SixP); - SetPlayerNameVisible(TextP6SixP, V6SixP); - SetPlayerNameVisible(TextP1DuetFourP, V1DuetFourP); - SetPlayerNameVisible(TextP2DuetFourP, V2DuetFourP); - SetPlayerNameVisible(TextP3DuetFourP, V3DuetFourP); - SetPlayerNameVisible(TextP4DuetFourP, V4DuetFourP); - SetPlayerNameVisible(TextP1DuetSixP, V1DuetSixP); - SetPlayerNameVisible(TextP2DuetSixP, V2DuetSixP); - SetPlayerNameVisible(TextP3DuetSixP, V3DuetSixP); - SetPlayerNameVisible(TextP4DuetSixP, V4DuetSixP); - SetPlayerNameVisible(TextP5DuetSixP, V5DuetSixP); - SetPlayerNameVisible(TextP6DuetSixP, V6DuetSixP); + end; + procedure SetTextVisibility(const Slots: TSlotArray; VisibleCount: integer; Visible: boolean); + var + J: integer; + begin + for J := 0 to High(Slots) do + ScreenSing.Text[Slots[J]].Visible := Visible and (J < VisibleCount) and ScreenSing.Settings.AvatarsVisible; + end; + procedure SetLyricsDuetColors(FirstPlayerIndex: integer); + var + PlayerColor: TRGB; + begin + PlayerColor := GetSingPlayerColor(FirstPlayerIndex - 1); + ScreenSing.LyricsDuetP1.LineColor_act.R := PlayerColor.R; + ScreenSing.LyricsDuetP1.LineColor_act.G := PlayerColor.G; + ScreenSing.LyricsDuetP1.LineColor_act.B := PlayerColor.B; + + PlayerColor := GetSingPlayerColor(FirstPlayerIndex); + ScreenSing.LyricsDuetP2.LineColor_act.R := PlayerColor.R; + ScreenSing.LyricsDuetP2.LineColor_act.G := PlayerColor.G; + ScreenSing.LyricsDuetP2.LineColor_act.B := PlayerColor.B; end; begin ScreenSing.Background.Draw; @@ -960,13 +693,6 @@ function TScreenSingView.Draw: boolean; // swap static textures to current screen ones SwapToScreen(ScreenAct); - UpdatePlayerNameVisibility; - - if (CurrentSong.isDuet) and (PlayersPlay = 4) then - begin - for I := 0 to High(ColPlayer) do - ColPlayer[I] := GetPlayerColor(Ini.PlayerColor[I]); - end; // draw background picture (if any, and if no visualizations) // when we don't check for visualizations the visualizations would @@ -978,91 +704,31 @@ function TScreenSingView.Draw: boolean; if (ScreenSing.fShowWebCam) then SingDrawWebCamFrame; - // set player names (for 2 screens and only singstar skin) - if ScreenAct = 1 then - begin - ScreenSing.Text[TextP1].Text := ScreenSing.PlayerNames[1]; - ScreenSing.Text[TextP1TwoP].Text := ScreenSing.PlayerNames[1]; - ScreenSing.Text[TextP1ThreeP].Text := ScreenSing.PlayerNames[1]; - ScreenSing.Text[TextP2R].Text := ScreenSing.PlayerNames[2]; - ScreenSing.Text[TextP2M].Text := ScreenSing.PlayerNames[2]; - ScreenSing.Text[TextP3R].Text := ScreenSing.PlayerNames[3]; - ScreenSing.Text[TextDuetP1ThreeP].Text := ScreenSing.PlayerDuetNames[1]; - ScreenSing.Text[TextDuetP2M].Text := ScreenSing.PlayerDuetNames[2]; - ScreenSing.Text[TextDuetP3R].Text := ScreenSing.PlayerDuetNames[3]; - ScreenSing.Text[TextP1FourP].Text := ScreenSing.PlayerNames[1]; - ScreenSing.Text[TextP2FourP].Text := ScreenSing.PlayerNames[2]; - ScreenSing.Text[TextP3FourP].Text := ScreenSing.PlayerNames[3]; - ScreenSing.Text[TextP4FourP].Text := ScreenSing.PlayerNames[4]; - ScreenSing.Text[TextP1DuetFourP].Text := ScreenSing.PlayerDuetNames[1]; - ScreenSing.Text[TextP2DuetFourP].Text := ScreenSing.PlayerDuetNames[2]; - ScreenSing.Text[TextP3DuetFourP].Text := ScreenSing.PlayerDuetNames[3]; - ScreenSing.Text[TextP4DuetFourP].Text := ScreenSing.PlayerDuetNames[4]; - ScreenSing.Text[TextP1SixP].Text := ScreenSing.PlayerNames[1]; - ScreenSing.Text[TextP2SixP].Text := ScreenSing.PlayerNames[2]; - ScreenSing.Text[TextP3SixP].Text := ScreenSing.PlayerNames[3]; - ScreenSing.Text[TextP4SixP].Text := ScreenSing.PlayerNames[4]; - ScreenSing.Text[TextP5SixP].Text := ScreenSing.PlayerNames[5]; - ScreenSing.Text[TextP6SixP].Text := ScreenSing.PlayerNames[6]; - ScreenSing.Text[TextP1DuetSixP].Text := ScreenSing.PlayerDuetNames[1]; - ScreenSing.Text[TextP2DuetSixP].Text := ScreenSing.PlayerDuetNames[2]; - ScreenSing.Text[TextP3DuetSixP].Text := ScreenSing.PlayerDuetNames[3]; - ScreenSing.Text[TextP4DuetSixP].Text := ScreenSing.PlayerDuetNames[4]; - ScreenSing.Text[TextP5DuetSixP].Text := ScreenSing.PlayerDuetNames[5]; - ScreenSing.Text[TextP6DuetSixP].Text := ScreenSing.PlayerDuetNames[6]; - - if (CurrentSong.isDuet) then - begin - if (PlayersPlay = 4) then - begin - ScreenSing.LyricsDuetP1.LineColor_act.R := ColPlayer[0].R; - ScreenSing.LyricsDuetP1.LineColor_act.G := ColPlayer[0].G; - ScreenSing.LyricsDuetP1.LineColor_act.B := ColPlayer[0].B; + LocalPlayerCount := GetScreenPlayerCount(PlayersPlay, Screens, ScreenAct); + LocalStartIndex := GetFirstPlayerIndexForScreen(PlayersPlay, Screens, ScreenAct) + 1; - ScreenSing.LyricsDuetP2.LineColor_act.R := ColPlayer[1].R; - ScreenSing.LyricsDuetP2.LineColor_act.G := ColPlayer[1].G; - ScreenSing.LyricsDuetP2.LineColor_act.B := ColPlayer[1].B; - end; - end; + for IterLayoutPlayerCount := 1 to UIni.IMaxPlayerCount do + begin + GetSingTextSlots(Self, IterLayoutPlayerCount, TextSlots); + SetTextVisibility(TextSlots, 0, false); end; - if ScreenAct = 2 then + GetSingTextSlots(Self, LocalPlayerCount, TextSlots); + for SlotIndex := 0 to LocalPlayerCount - 1 do begin - case PlayersPlay of - 4: - begin - ScreenSing.Text[TextP1TwoP].Text := ScreenSing.PlayerNames[3]; - ScreenSing.Text[TextP2R].Text := ScreenSing.PlayerNames[4]; - - if (CurrentSong.isDuet) and (PlayersPlay = 4) then - begin - ScreenSing.LyricsDuetP1.LineColor_act.R := ColPlayer[2].R; - ScreenSing.LyricsDuetP1.LineColor_act.G := ColPlayer[2].G; - ScreenSing.LyricsDuetP1.LineColor_act.B := ColPlayer[2].B; - - ScreenSing.LyricsDuetP2.LineColor_act.R := ColPlayer[3].R; - ScreenSing.LyricsDuetP2.LineColor_act.G := ColPlayer[3].G; - ScreenSing.LyricsDuetP2.LineColor_act.B := ColPlayer[3].B; - end; + SingPlayer := BuildSingPlayerTemplate(LocalPlayerCount, SlotIndex); + ApplySingPlayerTemplate( + SingPlayer, + PlayerFrameSlots[LocalPlayerCount, SlotIndex], + PlayerAvatarSlots[LocalPlayerCount, SlotIndex], + PlayerTextSlots[LocalPlayerCount, SlotIndex] + ); + end; + SetPlayerNameTexts(TextSlots, LocalStartIndex, IsCurrentSongDuet and (LocalPlayerCount >= 3)); + SetTextVisibility(TextSlots, LocalPlayerCount, true); - end; - 6: - begin - if (CurrentSong.isDuet) then - begin - ScreenSing.Text[TextDuetP1ThreeP].Text := ScreenSing.PlayerDuetNames[4]; - ScreenSing.Text[TextDuetP2M].Text := ScreenSing.PlayerDuetNames[5]; - ScreenSing.Text[TextDuetP3R].Text := ScreenSing.PlayerDuetNames[6]; - end - else - begin - ScreenSing.Text[TextP1ThreeP].Text := ScreenSing.PlayerNames[4]; - ScreenSing.Text[TextP2M].Text := ScreenSing.PlayerNames[5]; - ScreenSing.Text[TextP3R].Text := ScreenSing.PlayerNames[6]; - end; - end; - end; // case - end; // if + if IsCurrentSongDuet and (LocalPlayerCount = 2) then + SetLyricsDuetColors(LocalStartIndex); // retrieve current lyrics time, we have to store the value to avoid // that min- and sec-values do not match @@ -1219,19 +885,19 @@ function TScreenSingView.Draw: boolean; if (ScreenSing.Settings.TimeBarVisible) then DrawInfoLyricBar; + // draw scores + if (ScreenSing.Settings.ScoresVisible) and ((Ini.SingScores = 1) or (Party.bPartyGame)) then + ScreenSing.Scores.Draw; + // always draw custom items ScreenSing.Statics[StaticLyricsBar].Visible := ScreenSing.Settings.LyricsVisible; - ScreenSing.Statics[StaticLyricsBarDuet].Visible := ScreenSing.Settings.LyricsVisible and (CurrentSong.isDuet) and (PlayersPlay <> 1); + ScreenSing.Statics[StaticLyricsBarDuet].Visible := ScreenSing.Settings.LyricsVisible and IsCurrentSongDuet and (PlayersPlay <> 1); ScreenSing.Statics[StaticTimeBar].Visible := ScreenSing.Settings.TimeBarVisible; SingDraw; // goldennotestarstwinkle GoldenRec.SpawnRec; - // draw scores - if (ScreenSing.Settings.ScoresVisible) and ((Ini.SingScores = 1) or (Party.bPartyGame)) then - ScreenSing.Scores.Draw; - FadeMessage(); // draw pausepopup From 9ef2d10ee3274aa6a8cb1635748cf5ab06a81d33 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Tue, 12 May 2026 06:33:46 +0200 Subject: [PATCH 11/11] Expose generic player counts --- src/base/UIni.pas | 153 ++++++++++++++++++++++++++---------- src/screens/UScreenMain.pas | 5 +- 2 files changed, 112 insertions(+), 46 deletions(-) diff --git a/src/base/UIni.pas b/src/base/UIni.pas index 7a713978e..734ff08af 100644 --- a/src/base/UIni.pas +++ b/src/base/UIni.pas @@ -77,21 +77,7 @@ TInputDeviceConfig = record LATENCY_AUTODETECT = -1; // for field Latency DEFAULT_RESOLUTION = '800x600'; DEFAULT_THEME = 'Modern'; - // TODO: the menu options only go up to 6, but there are internals that still go up to 12 - // IMaxPlayerCount is untouched because lowering (or raising) it causes very strange behaviour, such as: - // * the game starts in a completely different language than the config specifies - // * the game crashes randomly - // 8 and 12 players have never worked at any point in history - // it all needs refactoring at some point anyway because: - // * a lot of code works with the _index_ of IPlayers (instead of just the number of actual players) - // * it should be possible to play with 5 players [without duplicating a lot of code] - // * there might be a valid usecase for 0 players - IMaxPlayerCount = 12; - // Switch colors for players 2 and 4, since player 2 line color is used - // for the second part in duet, and yellow (4) looks better than red (2) - DefaultPlayerColors: array[0..IMaxPlayerCount-1] of integer = (1, 4, 3, 2, 5, 6, 7, 8, 9, 10, 11, 12); - IPlayers: array[0..4] of UTF8String = ('1', '2', '3', '4', '6'); - IPlayersVals: array[0..4] of integer = ( 1 , 2 , 3 , 4 , 6 ); + IMaxPlayerCount = 24; type TUTF8StringArray = array of UTF8String; @@ -99,6 +85,7 @@ TInputDeviceConfig = record function GetNameTemplateIndexFromKey(const Key: cardinal): integer; function CreateNumericOptionArray(const FirstValue, LastValue: integer): TUTF8StringArray; +function GetPlayerColorOptionCount: integer; function TryGetMixedPlayerColorPair(ColorIndex: integer; out LeftColor, RightColor: integer): boolean; type @@ -132,7 +119,7 @@ TIni = class // Players or Teams colors SingColor: array[0..(IMaxPlayerCount-1)] of integer; - Name: array[0..15] of UTF8String; + Name: array[0..(IMaxPlayerCount-1)] of UTF8String; PlayerColor: array[0..(IMaxPlayerCount-1)] of integer; TeamColor: array[0..2] of integer; @@ -469,7 +456,6 @@ TIni = class IJukeboxTimebarMode: array[0..2] of UTF8String = ('Current', 'Remaining', 'Total'); // Recording options - IChannelPlayer: array[0..6] of UTF8String = ('Off', '1', '2', '3', '4', '5', '6'); IMicBoost: array[0..3] of UTF8String = ('Off', '+6dB', '+12dB', '+18dB'); // Webcam @@ -482,6 +468,9 @@ TIni = class *} var + IPlayers: TUTF8StringArray; + IPlayersVals: TIntegerArray; + IChannelPlayer: TUTF8StringArray; ILanguageTranslated: array of UTF8String; ILyricsFont: array of UTF8String; @@ -536,7 +525,7 @@ TIni = class ILyricsEffectTranslated: array[0..4] of UTF8String = ('Simple', 'Zoom', 'Slide', 'Ball', 'Shift'); INoteLinesTranslated: array[0..1] of UTF8String = ('Off', 'On'); IColorTranslated: array[0..8] of UTF8String = ('Blue', 'Green', 'Pink', 'Red', 'Violet', 'Orange', 'Yellow', 'Brown', 'Black'); - IPlayerColorTranslated: array[0..15] of UTF8String = ('Blue', 'Red', 'Green', 'Yellow', 'Orange', 'Pink', 'Violet', 'Brown', 'Gray', 'Dark Blue', 'Sky', 'Cyan', 'Flame', 'Orchid', 'Harlequin', 'Lime'); + IPlayerColorTranslated: TUTF8StringArray; // for lyric colors ILineTranslated: array[0..2] of UTF8String = ('Sing', 'Actual', 'Next'); @@ -567,13 +556,13 @@ TIni = class IJukeboxTimebarModeTranslated: array[0..2] of UTF8String = ('Current', 'Remaining', 'Total'); // Recording options - IChannelPlayerTranslated: array[0..IMaxPlayerCount] of UTF8String = ('Off', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'); + IChannelPlayerTranslated: TUTF8StringArray; IMicBoostTranslated: array[0..3] of UTF8String = ('Off', '+6dB', '+12dB', '+18dB'); // Network ISendNameTranslated: array[0..1] of UTF8String = ('Off', 'On'); IAutoModeTranslated: array[0..2] of UTF8String = ('Off', 'Send', 'Guardar'); - IAutoPlayerTranslated: array[0..IMaxPlayerCount] of UTF8String = ('Player 1', 'Player 2', 'Player 3', 'Player 4', 'Player 5', 'Player 6', 'Player 7', 'Player 8', 'Player 9', 'Player 10', 'Player 11', 'Player 12', 'All'); + IAutoPlayerTranslated: TUTF8StringArray; IAutoScoreEasyTranslated: array of UTF8String; IAutoScoreMediumTranslated: array of UTF8String; IAutoScoreHardTranslated: array of UTF8String; @@ -586,7 +575,7 @@ TIni = class IWebcamEffectTranslated: array [0..10] of UTF8String; // Name - IPlayerTranslated: array[0..(IMaxPlayerCount-1)] of UTF8String = ('Player 1', 'Player 2', 'Player 3', 'Player 4', 'Player 5', 'Player 6', 'Player 7', 'Player 8', 'Player 9', 'Player 10', 'Player 11', 'Player 12'); + IPlayerTranslated: TUTF8StringArray; IRed: array[0..255] of UTF8String; IGreen: array[0..255] of UTF8String; @@ -626,6 +615,20 @@ TSafeIniFile = class(TIniFile) IGNORE_INDEX = -1; BASE_PLAYER_COLOR_COUNT = 16; + PLAYER_COLOR_DEFAULTS: array[0..15] of UTF8String = ( + 'Blue', 'Red', 'Green', 'Yellow', 'Orange', 'Pink', 'Violet', 'Brown', + 'Gray', 'Dark Blue', 'Sky', 'Cyan', 'Flame', 'Orchid', 'Harlequin', 'Lime' + ); + + PLAYER_COLOR_TRANSLATION_KEYS: array[0..15] of UTF8String = ( + 'OPTION_VALUE_BLUE', 'OPTION_VALUE_RED', 'OPTION_VALUE_GREEN', + 'OPTION_VALUE_YELLOW', 'OPTION_VALUE_ORANGE', 'OPTION_VALUE_PINK', + 'OPTION_VALUE_VIOLET', 'OPTION_VALUE_BROWN', 'OPTION_VALUE_GRAY', + 'OPTION_VALUE_DARKBLUE', 'OPTION_VALUE_SKY', 'OPTION_VALUE_CYAN', + 'OPTION_VALUE_FLAME', 'OPTION_VALUE_ORCHID', 'OPTION_VALUE_HARLEQUIN', + 'OPTION_VALUE_GREENYELLOW' + ); + function GetNameTemplateIndexFromKey(const Key: cardinal): integer; begin case Key of @@ -661,6 +664,13 @@ function CreateNumericOptionArray(const FirstValue, LastValue: integer): TUTF8St Result[I] := IntToStr(FirstValue + I); end; +function GetPlayerColorOptionCount: integer; +begin + Result := (IMaxPlayerCount * 3 + 1) div 2; + if Result < BASE_PLAYER_COLOR_COUNT then + Result := BASE_PLAYER_COLOR_COUNT; +end; + function TryGetMixedPlayerColorPair(ColorIndex: integer; out LeftColor, RightColor: integer): boolean; var Remaining: integer; @@ -704,6 +714,60 @@ function TryGetMixedPlayerColorPair(ColorIndex: integer; out LeftColor, RightCol end; end; +function GetDefaultPlayerColor(PlayerIndex: integer): integer; +begin + if Length(IPlayerColorTranslated) = 0 then + Exit(0); + + Result := (PlayerIndex mod Length(IPlayerColorTranslated)) + 1; +end; + +procedure InitializePlayerOptionArrays; +var + I: integer; + LeftColor: integer; + RightColor: integer; +begin + SetLength(IPlayerColorTranslated, GetPlayerColorOptionCount); + for I := 0 to High(IPlayerColorTranslated) do + begin + if I < Length(PLAYER_COLOR_DEFAULTS) then + IPlayerColorTranslated[I] := PLAYER_COLOR_DEFAULTS[I] + else if TryGetMixedPlayerColorPair(I + 1, LeftColor, RightColor) then + IPlayerColorTranslated[I] := PLAYER_COLOR_DEFAULTS[LeftColor - 1] + ' + ' + PLAYER_COLOR_DEFAULTS[RightColor - 1] + else + IPlayerColorTranslated[I] := PLAYER_COLOR_DEFAULTS[I mod Length(PLAYER_COLOR_DEFAULTS)]; + end; + + SetLength(IPlayers, IMaxPlayerCount); + SetLength(IPlayersVals, IMaxPlayerCount); + for I := 0 to IMaxPlayerCount - 1 do + begin + IPlayers[I] := IntToStr(I + 1); + IPlayersVals[I] := I + 1; + end; + + SetLength(IChannelPlayer, IMaxPlayerCount + 1); + IChannelPlayer[0] := 'Off'; + for I := 1 to IMaxPlayerCount do + IChannelPlayer[I] := IntToStr(I); + + SetLength(IChannelPlayerTranslated, IMaxPlayerCount + 1); + SetLength(IAutoPlayerTranslated, IMaxPlayerCount + 1); + SetLength(IPlayerTranslated, IMaxPlayerCount); + + IChannelPlayerTranslated[0] := 'Off'; + for I := 1 to IMaxPlayerCount do + IChannelPlayerTranslated[I] := IntToStr(I); + + for I := 0 to IMaxPlayerCount - 1 do + IPlayerTranslated[I] := 'Player ' + IntToStr(I + 1); + + for I := 0 to IMaxPlayerCount - 1 do + IAutoPlayerTranslated[I] := 'Player ' + IntToStr(I + 1); + IAutoPlayerTranslated[IMaxPlayerCount] := 'All'; +end; + constructor TSafeIniFile.Create(const FileName, LogSource: string); begin inherited Create(FileName); @@ -753,7 +817,12 @@ procedure TIni.TranslateOptionValues; var I: integer; Zeros: string; + BasePlayerColorNames: array[0..BASE_PLAYER_COLOR_COUNT-1] of UTF8String; + LeftColor: integer; + RightColor: integer; begin + InitializePlayerOptionArrays; + // Load language file, fallback to config language if param is invalid if (Params.Language > -1) and (Params.Language < Length(ILanguage)) then ULanguage.Language.ChangeLanguage(ILanguage[Params.Language]) @@ -933,22 +1002,18 @@ procedure TIni.TranslateOptionValues; IColorTranslated[7] := ULanguage.Language.Translate('OPTION_VALUE_BROWN'); IColorTranslated[8] := ULanguage.Language.Translate('OPTION_VALUE_BLACK'); - IPlayerColorTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_BLUE'); - IPlayerColorTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_RED'); - IPlayerColorTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_GREEN'); - IPlayerColorTranslated[3] := ULanguage.Language.Translate('OPTION_VALUE_YELLOW'); - IPlayerColorTranslated[4] := ULanguage.Language.Translate('OPTION_VALUE_ORANGE'); - IPlayerColorTranslated[5] := ULanguage.Language.Translate('OPTION_VALUE_PINK'); - IPlayerColorTranslated[6] := ULanguage.Language.Translate('OPTION_VALUE_VIOLET'); - IPlayerColorTranslated[7] := ULanguage.Language.Translate('OPTION_VALUE_BROWN'); - IPlayerColorTranslated[8] := ULanguage.Language.Translate('OPTION_VALUE_GRAY'); - IPlayerColorTranslated[9] := ULanguage.Language.Translate('OPTION_VALUE_DARKBLUE'); - IPlayerColorTranslated[10] := ULanguage.Language.Translate('OPTION_VALUE_SKY'); - IPlayerColorTranslated[11] := ULanguage.Language.Translate('OPTION_VALUE_CYAN'); - IPlayerColorTranslated[12] := ULanguage.Language.Translate('OPTION_VALUE_FLAME'); - IPlayerColorTranslated[13] := ULanguage.Language.Translate('OPTION_VALUE_ORCHID'); - IPlayerColorTranslated[14] := ULanguage.Language.Translate('OPTION_VALUE_HARLEQUIN'); - IPlayerColorTranslated[15] := ULanguage.Language.Translate('OPTION_VALUE_GREENYELLOW'); + for I := 0 to High(BasePlayerColorNames) do + BasePlayerColorNames[I] := ULanguage.Language.Translate(PLAYER_COLOR_TRANSLATION_KEYS[I]); + + for I := 0 to High(IPlayerColorTranslated) do + begin + if I < Length(BasePlayerColorNames) then + IPlayerColorTranslated[I] := BasePlayerColorNames[I] + else if TryGetMixedPlayerColorPair(I + 1, LeftColor, RightColor) then + IPlayerColorTranslated[I] := BasePlayerColorNames[LeftColor - 1] + ' + ' + BasePlayerColorNames[RightColor - 1] + else + IPlayerColorTranslated[I] := BasePlayerColorNames[I mod Length(BasePlayerColorNames)]; + end; // Advanced ILoadAnimationTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); @@ -1060,9 +1125,12 @@ procedure TIni.TranslateOptionValues; for I:=0 to IMaxPlayerCount-1 do begin - IAutoPlayerTranslated[I] :=ULanguage.Language.Translate('OPTION_PLAYER_' + IntToStr(I)); + IAutoPlayerTranslated[I] := ULanguage.Language.Translate('OPTION_PLAYER_' + IntToStr(I + 1)); end; - IAutoPlayerTranslated[12] := ULanguage.Language.Translate('OPTION_ALL_PLAYERS'); + IAutoPlayerTranslated[IMaxPlayerCount] := ULanguage.Language.Translate('OPTION_ALL_PLAYERS'); + + for I := 0 to IMaxPlayerCount - 1 do + IPlayerTranslated[I] := ULanguage.Language.Translate('OPTION_PLAYER_' + IntToStr(I + 1)); // Webcam IWebcamFlipTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); @@ -1586,6 +1654,7 @@ procedure TIni.Load(); Result := 100; end; begin + InitializePlayerOptionArrays; LoadFontFamilyNames; ILyricsFont := FontFamilyNames; GamePath := Platform.GetGameUserPath; @@ -1600,12 +1669,12 @@ procedure TIni.Load(); Log.LogStatus('Using config : ' + FileName.ToNative, 'Ini'); IniFile := TMemIniFile.Create(FileName.ToNative); - for I := 0 to IMaxPlayerCount-1 do + for I := 0 to High(Name) do begin // Name Name[I] := IniFile.ReadString('Name', 'P'+IntToStr(I+1), 'Player'+IntToStr(I+1)); // Color Player - PlayerColor[I] := IniFile.ReadInteger('PlayerColor', 'P'+IntToStr(I+1), DefaultPlayerColors[I]); + PlayerColor[I] := IniFile.ReadInteger('PlayerColor', 'P'+IntToStr(I+1), GetDefaultPlayerColor(I)); // Initialize session sing colors from the saved player colors so they are usable before the first song SingColor[I] := PlayerColor[I]; // Avatar Player @@ -1621,7 +1690,7 @@ procedure TIni.Load(); // Templates for Names Mod for I := 0 to 2 do NameTeam[I] := IniFile.ReadString('NameTeam', 'T'+IntToStr(I+1), 'Team'+IntToStr(I+1)); - for I := 0 to 11 do + for I := 0 to High(NameTemplate) do NameTemplate[I] := IniFile.ReadString('NameTemplate', 'Name'+IntToStr(I+1), 'Template'+IntToStr(I+1)); // Players diff --git a/src/screens/UScreenMain.pas b/src/screens/UScreenMain.pas index 4408f14b3..3cc330cb1 100644 --- a/src/screens/UScreenMain.pas +++ b/src/screens/UScreenMain.pas @@ -172,10 +172,7 @@ function TScreenMain.ParseInput(PressedKey: Cardinal; CharCode: UCS4Char; begin if (Songs.SongList.Count >= 1) then begin - if (Ini.Players >= 0) and (Ini.Players <= 3) then - PlayersPlay := Ini.Players + 1; - if (Ini.Players = 4) then - PlayersPlay := 6; + PlayersPlay := UIni.IPlayersVals[Ini.Players]; if Ini.OnSongClick = sSelectPlayer then FadeTo(@ScreenSong)