aboutsummaryrefslogtreecommitdiffstats
path: root/src/screens
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens')
-rw-r--r--src/screens/UScreenCredits.pas1329
-rw-r--r--src/screens/UScreenEdit.pas164
-rw-r--r--src/screens/UScreenEditConvert.pas826
-rw-r--r--src/screens/UScreenEditHeader.pas445
-rw-r--r--src/screens/UScreenEditSub.pas1809
-rw-r--r--src/screens/UScreenLevel.pas139
-rw-r--r--src/screens/UScreenLoading.pas78
-rw-r--r--src/screens/UScreenMain.pas296
-rw-r--r--src/screens/UScreenName.pas284
-rw-r--r--src/screens/UScreenOpen.pas231
-rw-r--r--src/screens/UScreenOptions.pas234
-rw-r--r--src/screens/UScreenOptionsAdvanced.pas168
-rw-r--r--src/screens/UScreenOptionsGame.pas175
-rw-r--r--src/screens/UScreenOptionsGraphics.pas164
-rw-r--r--src/screens/UScreenOptionsLyrics.pas147
-rw-r--r--src/screens/UScreenOptionsRecord.pas836
-rw-r--r--src/screens/UScreenOptionsSound.pas187
-rw-r--r--src/screens/UScreenOptionsThemes.pas237
-rw-r--r--src/screens/UScreenPartyNewRound.pas334
-rw-r--r--src/screens/UScreenPartyOptions.pas284
-rw-r--r--src/screens/UScreenPartyPlayer.pas449
-rw-r--r--src/screens/UScreenPartyRounds.pas233
-rw-r--r--src/screens/UScreenPartyScore.pas334
-rw-r--r--src/screens/UScreenPartyWin.pas295
-rw-r--r--src/screens/UScreenPopup.pas308
-rw-r--r--src/screens/UScreenScore.pas1187
-rw-r--r--src/screens/UScreenSing.pas1071
-rw-r--r--src/screens/UScreenSong.pas2156
-rw-r--r--src/screens/UScreenSongJumpto.pas247
-rw-r--r--src/screens/UScreenSongMenu.pas664
-rw-r--r--src/screens/UScreenStatDetail.pas303
-rw-r--r--src/screens/UScreenStatMain.pas323
-rw-r--r--src/screens/UScreenTop5.pas307
33 files changed, 16244 insertions, 0 deletions
diff --git a/src/screens/UScreenCredits.pas b/src/screens/UScreenCredits.pas
new file mode 100644
index 00000000..90c0fa19
--- /dev/null
+++ b/src/screens/UScreenCredits.pas
@@ -0,0 +1,1329 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenCredits;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SysUtils,
+ SDL,
+ SDL_Image,
+ gl,
+ UMenu,
+ UDisplay,
+ UTexture,
+ UMusic,
+ UFiles,
+ UThemes,
+ UPath,
+ UGraphicClasses;
+
+{ beat detection constants and types }
+const
+ SubChannelCount = 32;
+ HistoryLength = 44;
+ SamplesPerChannel = (FFTSize div 2) div SubChannelCount;
+ BeatEnergyModifier = 80; // modifies detected energy
+ // higher values equal a more sensitive detection
+
+type
+ TEnergyHistory = array [0..HistoryLength-1] of single;
+ TSubchannelHistory = array [0..SubChannelCount-1] of TEnergyHistory;
+
+type
+ TCreditsStages=(InitialDelay, Intro, MainPart, Outro);
+
+ TScreenCredits = class(TMenu)
+ private
+ CreditsPath: IPath;
+
+ Credits_X: real;
+ Credits_Time: cardinal;
+ CTime_hold: cardinal;
+
+ credits_bg_tex: TTexture;
+ credits_bg_ovl: TTexture;
+ //credits_bg_logo: TTexture;
+ credits_names: array of TTexture;
+ intro_layer01: TTexture;
+ intro_layer02: TTexture;
+ intro_layer03: TTexture;
+ intro_layer04: TTexture;
+ intro_layer05: TTexture;
+ intro_layer06: TTexture;
+ intro_layer07: TTexture;
+ intro_layer08: TTexture;
+ intro_layer09: TTexture;
+ outro_bg: TTexture;
+ outro_esc: TTexture;
+ outro_exd: TTexture;
+
+ CurrentScrollStart, CurrentScrollEnd: integer;
+
+ CRDTS_Stage: TCreditsStages;
+
+ { beat detection }
+ SubChannelHistory: TSubchannelHistory;
+
+ { mouse movement easter eggs: }
+ MouseMoved: boolean;
+ MouseX, MouseY: double;
+
+ { saves last x and y angle for easter egg }
+ LogoAngleX, LogoAngleY: single;
+
+ procedure LoadNameTextures;
+
+ { draw different stages }
+ procedure DrawInitialDelay;
+
+ { Intro }
+ procedure DrawIntro;
+ procedure DrawLayeredLogo(Separation, Scale, AngleX, AngleY, AngleZ: single);
+
+ { Main }
+ procedure DrawMain;
+ procedure DrawMainBG;
+ procedure DrawFunkyText;
+
+ procedure DrawMainFG;
+
+ procedure DrawNames;
+ procedure DoLogoBling;
+
+ { Outro }
+ procedure DrawOutro;
+
+
+ { beat detection }
+ procedure DetectBeat;
+ protected
+ { beat detection stuff
+ protected cause we need this information for "on beat
+ effect"}
+ LastBeatTime: cardinal;
+ BeatDetected: boolean;
+ CTime: cardinal;
+ public
+ Fadeout: boolean;
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; override;
+ procedure OnShow; override;
+ procedure OnHide; override;
+ function Draw: boolean; override;
+ end;
+
+const
+ Funky_Text: string =
+ 'Grandma Deluxe has arrived! Thanks to Corvus5 for the massive work on UltraStar, Wome for the nice tune you are hearing, '+
+ 'all the people who put massive effort and work in new songs (do not forget UltraStar w/o songs would be nothing), ppl from '+
+ 'irc helping us - eBandit and Gabari, scene ppl who really helped instead of compiling and running away. Greetings to DennisTheMenace for betatesting, '+
+ 'Demoscene.tv, pouet.net, KakiArts, Sourceforge,..';
+
+{ texture names (loaded from gameshared/resources/credits}
+ CRDTS_BG_FILE = 'credits_v5_bg.png';
+ CRDTS_OVL_FILE = 'credits_v5_overlay.png';
+ INTRO_L01_FILE = 'intro-l-01.png';
+ INTRO_L02_FILE = 'intro-l-02.png';
+ INTRO_L03_FILE = 'intro-l-03.png';
+ INTRO_L04_FILE = 'intro-l-04.png';
+ INTRO_L05_FILE = 'intro-l-05.png';
+ INTRO_L06_FILE = 'intro-l-06.png';
+ INTRO_L07_FILE = 'intro-l-07.png';
+ INTRO_L08_FILE = 'intro-l-08.png';
+ INTRO_L09_FILE = 'intro-l-09.png';
+ OUTRO_BG_FILE = 'outro-bg.png';
+ OUTRO_ESC_FILE = 'outro-esc.png';
+ OUTRO_EXD_FILE = 'outro-exit-dark.png';
+
+{ some timings }
+ Delay_Before_Start = 20;
+ Intro_Flare_Start = 60;
+ Intro_Zoom_End = 149;
+ Intro_Stand_End = 155;
+ Intro_Separation_End = 170;
+ Intro_FadeToWhite_Start = 261;
+ Intro_Zoomout_Start = 271;
+ Main_Start = 271;
+ Main_OnBeatTwinkle_Start = 280;
+ Main_Names_Start = 359;
+ Main_Names_End = 2833;
+ Main_FadeOut_Start = 3096;
+ Tune_End = 3366;
+
+{ cosntants for developer names }
+
+type
+ TFadeEffect = procedure (const Tex: TTexture; Progress: double);
+ TCRTZ_Developer = record
+ Name: string; // developer name for texture loading (names_"devel".png)
+ Twinkle: boolean; // should there be twinkles on show
+ FadeIn: TFadeEffect; // fade in effect
+ Draw: TFadeEffect; // effect during draw
+ FadeOut: TFadeEffect; // fade out effect
+ end;
+
+{ effects are called with blending, texture and matrix prepared }
+procedure Effect_Draw (const Tex: TTexture; Progress: double);
+procedure Effect_OnBeatJitter (const Tex: TTexture; Progress: double);
+
+procedure Effect_Rotate_Left_Top (const Tex: TTexture; Progress: double);
+procedure Effect_Rotate_Right_Bot (const Tex: TTexture; Progress: double);
+procedure Effect_ZoomIn_Rotate (const Tex: TTexture; Progress: double);
+procedure Effect_ZoomOut_Shift (const Tex: TTexture; Progress: double);
+procedure Effect_Shift_Left (const Tex: TTexture; Progress: double);
+procedure Effect_Shift_Right_Top (const Tex: TTexture; Progress: double);
+procedure Effect_Flip_Bot (const Tex: TTexture; Progress: double);
+procedure Effect_Flip_Right_Top (const Tex: TTexture; Progress: double);
+procedure Effect_Flip_Right (const Tex: TTexture; Progress: double);
+procedure Effect_Flip_Right_Bot (const Tex: TTexture; Progress: double);
+procedure Effect_Rotate_Right_Top (const Tex: TTexture; Progress: double);
+procedure Effect_Shift_Weird (const Tex: TTexture; Progress: double);
+procedure Effect_Shift_Right_Bot (const Tex: TTexture; Progress: double);
+procedure Effect_Rotate_Right_Top2(const Tex: TTexture; Progress: double);
+procedure Effect_Flip_Left_Bot (const Tex: TTexture; Progress: double);
+procedure Effect_Flip_Right_Top2 (const Tex: TTexture; Progress: double);
+procedure Effect_Twinkle_Down (const Tex: TTexture; Progress: double);
+
+const
+ Developers: array[0..10] of TCRTZ_Developer = (
+ (Name: 'alexanders'; Twinkle: true; FadeIn: Effect_Rotate_Left_Top; Draw: Effect_OnBeatJitter; FadeOut: Effect_Rotate_Right_Bot),
+ (Name: 'blindy'; Twinkle: true; FadeIn: Effect_ZoomIn_Rotate; Draw: Effect_OnBeatJitter; FadeOut: Effect_ZoomOut_Shift),
+ (Name: 'brunzel'; Twinkle: true; FadeIn: Effect_Shift_Left; Draw: Effect_Draw; FadeOut: Effect_Shift_Right_Top),
+ (Name: 'canni'; Twinkle: true; FadeIn: Effect_Flip_Bot; Draw: Effect_Draw; FadeOut: Effect_Flip_Right_Top),
+ (Name: 'hennymcc'; Twinkle: true; FadeIn: Effect_Flip_Right; Draw: Effect_OnBeatJitter; FadeOut: Effect_Flip_Right_Bot),
+ (Name: 'jaybinks'; Twinkle: true; FadeIn: Effect_Rotate_Right_Top; Draw: Effect_OnBeatJitter; FadeOut: Effect_Shift_Weird),
+ (Name: 'krueger'; Twinkle: true; FadeIn: Effect_Shift_Right_Bot; Draw: Effect_OnBeatJitter; FadeOut: Effect_Rotate_Right_Top2),
+ (Name: 'mezzox'; Twinkle: true; FadeIn: Effect_Flip_Left_Bot; Draw: Effect_OnBeatJitter; FadeOut: Effect_Flip_Right_Top),
+ (Name: 'mischi'; Twinkle: true; FadeIn: Effect_Shift_Weird; Draw: Effect_OnBeatJitter; FadeOut: Effect_Flip_Bot),
+ (Name: 'mog'; Twinkle: false; FadeIn: Effect_Twinkle_Down; Draw: Effect_OnBeatJitter; FadeOut: Effect_ZoomIn_Rotate),
+ (Name: 'whiteshark'; Twinkle: true; FadeIn: Effect_Rotate_Right_Top2; Draw: Effect_OnBeatJitter; FadeOut: Effect_Shift_Left)
+ );
+
+ { name specific times }
+ TimePerName = (Main_Names_End - Main_Names_Start) div Length(Developers);
+ NameFadeTime = 12; // duration of fade in/out in 1/100 secs
+ NameWaitTime = 5; // delay between fade out and fade in of the next devel in 1/100 secs
+ NameTwinkleTime = 2; // duration of star effects in 1/100 secs
+ BeatJitterTime = 3; // duration of on beat jitter effect
+ { position at which the names show up
+ note: due to use of translate this is the center
+ of the names not the upper left corner as usual }
+ NameX = 223;
+ NameY = 329;
+ NameW = 326;
+ NameH = 258;
+
+implementation
+
+uses
+ Math,
+ ULog,
+ UGraphic,
+ UMain,
+ UIni,
+ USongs,
+ Textgl,
+ ULanguage,
+ UCommon,
+ UPathUtils;
+
+function TScreenCredits.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ case PressedKey of
+
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE,
+ SDLK_RETURN :
+ begin
+ FadeTo(@ScreenMain);
+ AudioPlayback.PlaySound(SoundLib.Back);
+ end;
+{
+ SDLK_SPACE:
+ begin
+ setlength(CTime_hold,length(CTime_hold)+1);
+ CTime_hold[high(CTime_hold)]:=CTime;
+ end;
+}
+ end; // esac
+ end; // fi
+end;
+
+function TScreenCredits.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean;
+begin
+ Result := inherited ParseMouse(MouseButton, BtnDown, X, Y);
+
+ { calculate mouse coordinates from -1 to 1
+ relative to screen center }
+ MouseX := (X - (ScreenW / Screens) / 2) / ((ScreenW / Screens) / 2);
+ MouseY := (Y - ScreenH / 2) / (ScreenH / 2);
+
+ MouseMoved := true;
+end;
+
+procedure TScreenCredits.LoadNameTextures;
+ var I: integer;
+begin
+ SetLength(credits_names, Length(Developers));
+
+ for I := 0 to High(Developers) do
+ begin
+ credits_names[I] := Texture.LoadTexture(CreditsPath.Append('names_' + Developers[I].Name + '.png'), TEXTURE_TYPE_TRANSPARENT, 0);
+ end;
+end;
+
+constructor TScreenCredits.Create;
+begin
+ inherited Create;
+
+ CreditsPath := ResourcesPath.Append('credits', pdAppend);
+
+ credits_bg_tex := Texture.LoadTexture(CreditsPath.Append(CRDTS_BG_FILE), TEXTURE_TYPE_PLAIN, 0);
+ credits_bg_ovl := Texture.LoadTexture(CreditsPath.Append(CRDTS_OVL_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+
+ LoadNameTextures;
+
+ intro_layer01 := Texture.LoadTexture(CreditsPath.Append(INTRO_L01_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ intro_layer02 := Texture.LoadTexture(CreditsPath.Append(INTRO_L02_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ intro_layer03 := Texture.LoadTexture(CreditsPath.Append(INTRO_L03_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ intro_layer04 := Texture.LoadTexture(CreditsPath.Append(INTRO_L04_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ intro_layer05 := Texture.LoadTexture(CreditsPath.Append(INTRO_L05_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ intro_layer06 := Texture.LoadTexture(CreditsPath.Append(INTRO_L06_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ intro_layer07 := Texture.LoadTexture(CreditsPath.Append(INTRO_L07_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ intro_layer08 := Texture.LoadTexture(CreditsPath.Append(INTRO_L08_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ intro_layer09 := Texture.LoadTexture(CreditsPath.Append(INTRO_L09_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+
+ outro_bg := Texture.LoadTexture(CreditsPath.Append(OUTRO_BG_FILE), TEXTURE_TYPE_PLAIN, 0);
+ outro_esc := Texture.LoadTexture(CreditsPath.Append(OUTRO_ESC_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+ outro_exd := Texture.LoadTexture(CreditsPath.Append(OUTRO_EXD_FILE), TEXTURE_TYPE_TRANSPARENT, 0);
+
+ CRDTS_Stage:=InitialDelay;
+end;
+
+procedure TScreenCredits.OnShow;
+begin
+ inherited;
+
+ { pause background music }
+ SoundLib.PauseBgMusic;
+
+ CRDTS_Stage := InitialDelay;
+ CTime := 0;
+ Credits_X := 580;
+
+ { open credits tune, we play it after initial delay }
+ AudioPlayback.Open(soundpath.Append('wome-credits-tune.mp3')); // thank you wetue
+
+ { reset twinkling stars }
+ GoldenRec.KillAll;
+
+ { reset mouse coords }
+ MouseMoved := false;
+ MouseX := 0;
+ MouseY := 0;
+
+ { hide cursor }
+ Display.SetCursor;
+end;
+
+procedure TScreenCredits.OnHide;
+begin
+ AudioPlayback.Stop;
+
+ { show cursor }
+ Display.SetCursor;
+
+ SoundLib.StartBgMusic;
+end;
+
+function TScreenCredits.Draw: boolean;
+ var
+ T: cardinal;
+begin
+ Result := true;
+
+ // reset beat detection
+ BeatDetected := false;
+
+ T := SDL_GetTicks() div 33;
+ if T <> Credits_Time then
+ begin
+ Credits_Time := T;
+ inc(CTime);
+ inc(CTime_hold);
+ Credits_X := Credits_X-2;
+
+ if (CRDTS_Stage = InitialDelay) and (CTime >= Delay_Before_Start) then
+ begin
+ CRDTS_Stage := Intro;
+ CTime := 0;
+ AudioPlayback.Play;
+ end
+ else if (CRDTS_Stage = Intro) and (CTime >= Main_Start) then
+ begin
+ CRDTS_Stage := MainPart;
+ end
+ else if (CRDTS_Stage = MainPart) and (CTime >= Tune_End) then
+ begin
+ CRDTS_Stage := Outro;
+ end;
+
+ // dis does teh muiwk y0r to be translated :-)
+ DetectBeat;
+ end;
+
+ case CRDTS_Stage of
+ InitialDelay: DrawInitialDelay;
+ Intro: DrawIntro;
+ MainPart: DrawMain;
+ Outro: DrawOutro;
+ end;
+
+ // make the stars shine
+ GoldenRec.Draw;
+end;
+
+procedure TScreenCredits.DrawInitialDelay;
+begin
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+end;
+
+procedure TScreenCredits.DrawIntro;
+ var
+ Separation, Scale,
+ AngleX, AngleY, AngleZ: single;
+ FlareX, FlareY: single;
+ I: integer;
+begin
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+
+ { rotate logo anti clockwise and make it grow }
+ if (CTime >= Intro_Separation_End) then
+ begin
+ Separation := 1;
+ Scale := 1 + sqr(CTime - Intro_Separation_End) / (32 * (Main_Start - Intro_Separation_End));
+ AngleX := 0;
+ AngleY := 0;
+ AngleZ := 20 * sqr(CTime - Intro_Separation_End) / sqr((Main_Start - Intro_Separation_End) / 2);
+ end
+
+ { separate layers }
+ else if (CTime >= Intro_Stand_End) then
+ begin
+ Separation := 0.5 + 0.5 * (CTime - Intro_Stand_End) / (Intro_Separation_End - Intro_Stand_End);
+ Scale := 1;
+ AngleX := 0;
+ AngleY := 0;
+ AngleZ := 0;
+ end
+
+ { stand still }
+ else if (CTime >= Intro_Zoom_End) then
+ begin
+ Separation := 0.5;
+ Scale := 1;
+ AngleX := 0;
+ AngleY := 0;
+ AngleZ := 0;
+ end
+
+ { rotate left }
+ else
+ begin
+ Separation := 0.5 + 0.5 * (Intro_Zoom_End - CTime) / (Intro_Zoom_End);
+ Scale := 1;
+ AngleX := 10 * (Intro_Zoom_End - CTime) / (Intro_Zoom_End);
+ AngleY := 20 * (Intro_Zoom_End - CTime) / (Intro_Zoom_End);
+ AngleZ := 0;
+ end;
+
+ { the user moved the mouse, overwrite X and Y angle with
+ according to mouse position }
+ if (MouseMoved) then
+ begin
+ // calculate destination angle
+ AngleX := 30 * MouseY;
+ AngleY := 30 * MouseX;
+
+ { move angle towards destination }
+ if not SameValue(LogoAngleX, AngleX, 0.001) then
+ AngleX := LogoAngleX + 0.05 * (AngleX - LogoAngleX);
+
+ if not SameValue(LogoAngleY, AngleY, 0.001) then
+ AngleY := LogoAngleY + 0.05 * (AngleY - LogoAngleY);
+ end;
+
+ // save last angle
+ LogoAngleX := AngleX;
+ LogoAngleY := AngleY;
+
+ DrawLayeredLogo(Separation, Scale, AngleX, AngleY, AngleZ);
+
+ { do some sparkling effects }
+ if (CTime < Intro_Zoom_End) and (CTime > Intro_Flare_Start) then
+ begin
+ for I := 1 to 3 do
+ begin
+ FlareX := 410 + Floor((CTime - Intro_Flare_Start) / (Intro_Zoom_End - Intro_Flare_Start) * (536 - 410)) + RandomRange(-5, 5);
+ FlareY := Floor((Intro_Zoom_End - CTime) / 22) + RandomRange(285, 301);
+ GoldenRec.Spawn(FlareX, FlareY, 1, 16, 0, -1, Flare, 0);
+ end;
+ end;
+
+ { fade to white at end }
+ if Ctime > Intro_FadeToWhite_Start then
+ begin
+ glColor4f(1, 1, 1, sqr(CTime - Intro_FadeToWhite_Start) * (CTime - Intro_FadeToWhite_Start) / sqr(Main_Start - Intro_FadeToWhite_Start));
+ glEnable(GL_BLEND);
+ glBegin(GL_QUADS);
+ glVertex2f( 0, 0);
+ glVertex2f( 0, 600);
+ glVertex2f(800, 600);
+ glVertex2f(800, 0);
+ glEnd;
+ glDisable(GL_BLEND);
+ end;
+end;
+
+procedure Start3D;
+begin
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix;
+ glLoadIdentity;
+ glFrustum(-0.3 * 4 / 3, 0.3 * 4 / 3, -0.3, 0.3, 1, 1000);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity;
+end;
+
+procedure End3D;
+begin
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix;
+ glMatrixMode(GL_MODELVIEW);
+end;
+
+procedure TScreenCredits.DrawLayeredLogo(Separation, Scale, AngleX, AngleY, AngleZ: single);
+ var
+ TotalAngle: single;
+begin
+ Start3D;
+ glPushMatrix;
+
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ glTranslatef(0, 0, -5 + 0.5 * Separation);
+
+ TotalAngle := Abs(AngleX) + Abs(AngleY) + Abs(AngleZ);
+ if not isZero(TotalAngle) then
+ glRotatef(TotalAngle, AngleX / TotalAngle, AngleY / TotalAngle, AngleZ / TotalAngle);
+
+ glScalef(Scale, Scale, 1);
+
+ glScalef(4/3, -1, 1);
+ glColor4f(1, 1, 1, 1);
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer01.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.4 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.4 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.4 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.4 * Separation);
+ glEnd;
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer02.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.3 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.3 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.3 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.3 * Separation);
+ glEnd;
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer03.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.2 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.2 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.2 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.2 * Separation);
+ glEnd;
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer04.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.1 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.1 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.1 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.1 * Separation);
+ glEnd;
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer05.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, 0 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, 0 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, 0 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, 0 * Separation);
+ glEnd;
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer06.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, 0.1 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, 0.1 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, 0.1 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, 0.1 * Separation);
+ glEnd;
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer07.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, 0.2 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, 0.2 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, 0.2 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, 0.2 * Separation);
+ glEnd;
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer08.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, 0.3 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, 0.3 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, 0.3 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, 0.3 * Separation);
+ glEnd;
+
+ glBindTexture(GL_TEXTURE_2D, intro_layer09.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, 0.22 * Separation);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, 0.22 * Separation);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, 0.22 * Separation);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, 0.22 * Separation);
+ glEnd;
+
+ glDisable(Gl_Texture_2D);
+ glDisable(GL_BLEND);
+
+ glPopMatrix;
+ End3D;
+end;
+
+procedure TScreenCredits.DrawMain;
+begin
+ DrawMainBG;
+ DrawFunkyText;
+ DrawNames;
+ DrawMainFG;
+ DoLogoBling;
+
+ // fade out at end of main part
+ if (Ctime > Main_FadeOut_Start) then
+ begin
+ glColor4f(0, 0, 0, (CTime - Main_FadeOut_Start) / (Tune_End - Main_FadeOut_Start));
+ glEnable(GL_BLEND);
+ glBegin(GL_QUADS);
+ glVertex2f( 0, 0);
+ glVertex2f( 0, 600);
+ glVertex2f(800, 600);
+ glVertex2f(800, 0);
+ glEnd;
+ glDisable(GL_BLEND);
+ end;
+end;
+
+procedure TScreenCredits.DrawMainBG;
+begin
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ glColor4f(1, 1, 1, 1);
+ glBindTexture(GL_TEXTURE_2D, credits_bg_tex.TexNum);
+ glBegin(Gl_Quads);
+ glTexCoord2f( 0, 0); glVertex2f( 0, 0);
+ glTexCoord2f( 0, 600/1024); glVertex2f( 0, RenderH);
+ glTexCoord2f(800/1024, 600/1024); glVertex2f(RenderW, RenderH);
+ glTexCoord2f(800/1024, 0); glVertex2f(RenderW, 0);
+ glEnd;
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+end;
+
+procedure TScreenCredits.DrawFunkyText;
+var
+ S: integer;
+ X, Y, A: real;
+ visibleText: string;
+begin
+ SetFontSize(30);
+
+ // init ScrollingText
+ if (CTime = Main_Start) then
+ begin
+ // set position of text
+ Credits_X := 600;
+ CurrentScrollStart := 1;
+ CurrentScrollEnd := 1;
+ end;
+
+ if (CTime > Main_Start) and
+ (CurrentScrollStart < length(Funky_Text)) then
+ begin
+ X := 0;
+ visibleText := Copy(Funky_Text, CurrentScrollStart, CurrentScrollEnd);
+
+ for S := 1 to length(visibleText) do
+ begin
+ Y := abs(sin((Credits_X + X) * 0.93 { * (((Credits_X + X)) / 1200) } / 100 * pi));
+ SetFontPos(Credits_X + X, 538 - Y * (Credits_X + X) * (Credits_X + X) * (Credits_X + X) / 1000000);
+
+ if (Credits_X + X > 32) then
+ A := 17
+ else if (Credits_X + X >= 15) then
+ A := Credits_X + X - 15
+ else
+ A := 0;
+
+ glColor4f(230 / 255 - 40 / 255 + Y * (Credits_X + X)/ 900,
+ 200 / 255 - 30 / 255 + Y * (Credits_X + X)/ 1000,
+ 155 / 255 - 20 / 255 + Y * (Credits_X + X)/ 1100,
+ A / 17);
+ glPrint(visibleText[S]);
+ X := X + glTextWidth(visibleText[S]);
+ end;
+
+ if (Credits_X < 0) and (CurrentScrollStart < length(Funky_Text)) then
+ begin
+ Credits_X := Credits_X + glTextWidth(Funky_Text[CurrentScrollStart]);
+ inc(CurrentScrollStart);
+ end;
+
+ visibleText := Copy(Funky_Text, CurrentScrollStart, CurrentScrollEnd);
+
+ if (Credits_X + glTextWidth(visibleText) < 600) and
+ (CurrentScrollEnd < length(Funky_Text)) then
+ begin
+ inc(CurrentScrollEnd);
+ end;
+ end;
+{
+// timing hack
+ X:=5;
+ SetFontStyle(2);
+ SetFontItalic(false);
+ SetFontSize(27);
+ glColor4f(1, 1, 1, 1);
+ for S := 0 to high(CTime_hold) do
+ begin
+ visibleText := inttostr(CTime_hold[S]);
+ SetFontPos (500, X);
+ glPrint(visibleText[0]);
+ X := X + 20;
+ end;
+}
+end;
+
+procedure TScreenCredits.DrawNames;
+ var
+ Dev: integer;
+ Ticks: integer;
+ DevTicks: integer;
+ TwinkleW, TwinkleH: integer;
+begin
+ Ticks := (CTime - Main_Names_Start);
+ Dev := Ticks div TimePerName;
+ DevTicks := Ticks mod TimePerName;
+
+ {// debug stuff
+ SetFontPos(20, 20);
+ glPrint('Ticks: ' + IntToStr(Ticks));
+ SetFontPos(20, 45);
+ glPrint('Dev: ' + IntToStr(Dev));
+ SetFontPos(20, 70);
+ glPrint('DevTicks: ' + IntToStr(DevTicks)); //}
+
+ if (Ticks >= 0) and (Dev <= High(Developers)) then
+ begin
+ { spawn twinkling stars }
+ if (Developers[Dev].Twinkle) and (DevTicks >= NameFadeTime) and (DevTicks <= NameFadeTime + NameTwinkleTime) then
+ begin
+ TwinkleW := Round(NameW * 0.6);
+ TwinkleH := Round(NameH * 0.6);
+
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 0);
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 1);
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 5);
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 0);
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 1);
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 5);
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 0);
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 1);
+ GoldenRec.Spawn(NameX + RandomRange(-TwinkleW, TwinkleW), NameY + RandomRange(-TwinkleH, TwinkleH), 1, 16, 0, -1, PerfectLineTwinkle, 5);
+ end;
+
+ { prepare drawing }
+ glPushMatrix;
+ glTranslatef(NameX, NameY, 0);
+ glBindTexture(GL_TEXTURE_2D, credits_names[Dev].TexNum);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_2D);
+
+ // calculate progress and call effect
+ if (DevTicks <= NameFadeTime) then
+ Developers[Dev].FadeIn(credits_names[Dev], DevTicks / NameFadeTime)
+ else if (DevTicks >= TimePerName - NameFadeTime - NameWaitTime) then
+ begin
+ if (DevTicks < TimePerName - NameWaitTime) then
+ Developers[Dev].FadeOut(credits_names[Dev], ((TimePerName - NameWaitTime) - DevTicks) / NameFadeTime);
+ end
+ else
+ Developers[Dev].Draw(credits_names[Dev], (DevTicks - NameFadeTime) / (TimePerName - NameFadeTime * 2 - NameWaitTime));
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+ glPopMatrix;
+ end;
+end;
+
+procedure TScreenCredits.DrawMainFG;
+begin
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glColor4f(1, 1, 1, 1);
+ glBindTexture(GL_TEXTURE_2D, credits_bg_ovl.TexNum);
+ glBegin(gl_Quads);
+ glTexCoord2f( 0, 0); glVertex2f(800-393, 0);
+ glTexCoord2f( 0, 600/1024); glVertex2f(800-393, 600);
+ glTexCoord2f(393/512, 600/1024); glVertex2f(800, 600);
+ glTexCoord2f(393/512, 0); glVertex2f(800, 0);
+ glEnd;
+
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+end;
+
+procedure TScreenCredits.DoLogoBling;
+ const
+ myLogoCoords: array[0..27,0..1] of cardinal = (
+ ( 39,32),( 84,32),(100,16),(125,24),
+ (154,31),(156,58),(168,32),(203,36),
+ (258,34),(251,50),(274,93),(294,84),
+ (232,54),(278,62),(319,34),(336,92),
+ (347,23),(374,32),(377,58),(361,83),
+ (385,91),(405,91),(429,35),(423,51),
+ (450,32),(485,34),(444,91),(486,93)
+ );
+ var
+ Coords: integer;
+ StartFrame: integer;
+begin
+ if (CTime > Main_OnBeatTwinkle_Start ) and
+ (CTime < Main_FadeOut_Start) then
+ begin
+ { spawn stars only in frames where a beat was detected }
+ if BeatDetected then
+ begin
+ StartFrame := RandomRange(6, 16);
+ Coords := RandomRange(0, 27);
+
+ GoldenRec.Spawn(myLogoCoords[Coords,0], myLogoCoords[Coords,1], 16-StartFrame, StartFrame, 0, -1, PerfectNote, 0);
+ end;
+ end;
+end;
+
+procedure TScreenCredits.DrawOutro;
+begin
+ if CTime = Tune_End then
+ begin
+ CTime_hold := 0;
+ AudioPlayback.Stop;
+ AudioPlayback.Open(SoundPath.Append('credits-outro-tune.mp3'));
+ AudioPlayback.SetVolume(0.2);
+ AudioPlayback.SetLoop(true);
+ AudioPlayback.Play;
+ end;
+
+ if CTime_hold > 231 then
+ begin
+ AudioPlayback.Play;
+ Ctime_hold := 0;
+ end;
+
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+
+ // do something useful
+ // outro background
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ glColor4f(1, 1, 1, 1);
+ glBindTexture(GL_TEXTURE_2D, outro_bg.TexNum);
+ glBegin(gl_quads);
+ glTexCoord2f( 0, 0); glVertex2f( 0, 0);
+ glTexCoord2f( 0, 600/1024); glVertex2f( 0, 600);
+ glTexCoord2f(800/1024, 600/1024); glVertex2f(800, 600);
+ glTexCoord2f(800/1024, 0); glVertex2f(800, 0);
+ glEnd;
+
+ // outro overlays
+ glColor4f(1, 1, 1, (2 + sin(CTime / 15)) / 3);
+ glBindTexture(GL_TEXTURE_2D, outro_esc.TexNum);
+ glBegin(Gl_Quads);
+ glTexCoord2f( 0, 0); glVertex2f( 0, 0);
+ glTexCoord2f( 0, 223/256); glVertex2f( 0, 223);
+ glTexCoord2f(487/512, 223/256); glVertex2f(487, 223);
+ glTexCoord2f(487/512, 0); glVertex2f(487, 0);
+ glEnd;
+
+ if (RandomRange(0,20) <= 18) then
+ begin
+ glColor4f(1, 1, 1, 1);
+ glBindTexture(GL_TEXTURE_2D, outro_exd.TexNum);
+ glBegin(Gl_Quads);
+ glTexCoord2f( 0, 0); glVertex2f(800-310, 600-247);
+ glTexCoord2f( 0, 247/256); glVertex2f(800-310, 600 );
+ glTexCoord2f(310/512, 247/256); glVertex2f(800, 600 );
+ glTexCoord2f(310/512, 0); glVertex2f(800, 600-247);
+ glEnd;
+ end;
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+
+ // outro scrollers?
+ // ...
+end;
+
+{ name effects }
+{ effects are called with blending texture and matrix prepared }
+procedure Effect_Draw (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, 1);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_OnBeatJitter (const Tex: TTexture; Progress: double);
+ var
+ Diff: cardinal;
+ Alpha: double;
+begin
+ Diff := ScreenCredits.CTime - ScreenCredits.LastBeatTime;
+ if (Diff < BeatJitterTime) then
+ Alpha := 0.5 + 0.5 * Diff / BeatJitterTime
+ else
+ Alpha := 1;
+
+ glColor4f(1, 1, 1, Alpha);
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Rotate_Left_Top (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ gltranslatef(-NameX, 0, 0);
+ glrotatef(Progress * 90 + 270, 0, 0, 1);
+ gltranslatef(NameX, 0, 0);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Rotate_Right_Bot (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ gltranslatef(NameX, 0, 0);
+ glrotatef((Progress - 1) * 90, 0, 0, 1);
+ gltranslatef(-NameX, 0, 0);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_ZoomIn_Rotate (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ glscalef(sqr(Progress), sqr(Progress), sqr(Progress));
+ glrotatef(Progress * 360, 0, 0, 1);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_ZoomOut_Shift (const Tex: TTexture; Progress: double);
+ var
+ X: double;
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ X := (1 - Progress);
+ gltranslatef(X * 300, -X * 100, 0);
+ glscalef(1 + X, 1 + X, 1 + X);
+ glrotatef(X * 90, 0, 0, 1);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Shift_Left (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ glTranslatef((Progress - 1) * 210, 0, 0);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Shift_Right_Top (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ glTranslatef((1 - Progress) * 210, (Progress - 1) * 105, 0);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Flip_Bot (const Tex: TTexture; Progress: double);
+ var
+ X: double;
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ X := NameH * (1 - Progress);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2 - 1.5 * X, -NameH/2 + 1.5 * X);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2 + 1.5 * X, -NameH/2 + 1.5 * X);
+ glEnd;
+end;
+
+procedure Effect_Flip_Right_Top (const Tex: TTexture; Progress: double);
+ var
+ X: double;
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ X := NameW * (1 - Progress);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2 + X, -NameH/2 - X/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2 + X, NameH/2 - (X * 1.5 * NameH / NameW));
+ glTexCoord2f(1, 1); glVertex2f( NameW/2 + X, NameH/2 + X / 4);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2 + X, -NameH/2 - X / 4);
+ glEnd;
+end;
+
+procedure Effect_Flip_Right (const Tex: TTexture; Progress: double);
+ var
+ X: double;
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ X := NameW * (1 - Progress);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2 - X, NameH/2 + X * 1.5);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2 - X, -NameH/2 - X * 1.5);
+ glEnd;
+end;
+
+procedure Effect_Flip_Right_Bot (const Tex: TTexture; Progress: double);
+ var
+ X: double;
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ X := NameW * (1 - Progress);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2 + X * 1.5, -NameH/2 + X * 1.5);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2 + X * 1.2, NameH/2 + X);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2 + X / 2, NameH/2 + X / 4);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2 + X * 1.5, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Rotate_Right_Top (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ glTranslatef(NameX, 0, 0);
+ glrotatef((1 - Progress) * 90, 0, 0, 1);
+ glTranslatef(-NameX, 0, 0);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Shift_Weird (const Tex: TTexture; Progress: double);
+ var
+ X: double;
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ X := (Progress - 1);
+
+ glTranslatef(X * 200, X * 100, 0);
+ glScalef(Progress, Progress, Progress);
+ glRotatef(X * 90, 0, 0, 1);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Shift_Right_Bot (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ glTranslatef((1 - Progress) * 200, (1 - Progress) * 100, 0);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Rotate_Right_Top2 (const Tex: TTexture; Progress: double);
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ glTranslatef(0, -NameX, 0);
+ glRotatef((Progress - 1) * 90, 0, 0, 1);
+ glTranslatef(0, NameX, 0);
+ glRotatef((1 - Progress) * 90, 0, 0, 1);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH/2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2, NameH/2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2, NameH/2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH/2);
+ glEnd;
+end;
+
+procedure Effect_Flip_Left_Bot (const Tex: TTexture; Progress: double);
+ var
+ X: double;
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ X := (1 - Progress) * NameW;
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2 - X, -NameH/2 + X / 4);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2 - X / 4, NameH/2 + X / 4);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2 - X * 1.2, NameH/2 + X / 2);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2 - X * 1.5, -NameH/2 + X * 1.5);
+ glEnd;
+end;
+
+procedure Effect_Flip_Right_Top2 (const Tex: TTexture; Progress: double);
+ var
+ X: double;
+begin
+ glColor4f(1, 1, 1, Progress);
+
+ X := (1 - Progress) * NameW;
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2 + X, -NameH/2 - X / 2);
+ glTexCoord2f(0, 1); glVertex2f(-NameW/2 + X, NameH/2 + X / 2);
+ glTexCoord2f(1, 1); glVertex2f( NameW/2 + X / 4, NameH/2 - X / 4);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2 + X / 4, -NameH/2 + X / 4);
+ glEnd;
+end;
+
+procedure Effect_Twinkle_Down (const Tex: TTexture; Progress: double);
+begin
+ // draw name
+ glColor4f(1, 1, 1, 1);
+
+ glTranslatef(0, NameH/2, 0);
+
+ glBegin(gl_Quads);
+ glTexCoord2f(0, 0); glVertex2f(-NameW/2, -NameH * Progress);
+ glTexCoord2f(0, Progress); glVertex2f(-NameW/2, 0);
+ glTexCoord2f(1, Progress); glVertex2f( NameW/2, 0);
+ glTexCoord2f(1, 0); glVertex2f( NameW/2, -NameH * Progress);
+ glEnd;
+
+ //spawn some stars on the edge
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 0);
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 1);
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 5);
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 0);
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 1);
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 5);
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 0);
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 1);
+ GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 5);
+end;
+
+{ beat detection algorithm
+ based on a tutorial from Frédéric Patin on gamedev.net
+ http://www.gamedev.net/reference/programming/features/beatdetection/default.asp }
+
+{ calculates average value of a history buffer }
+function Average(History: TEnergyHistory): single;
+ var I: integer;
+begin
+ Result := 0;
+
+ for I := 0 to HistoryLength - 1 do
+ Result := Result + History[I];
+
+ Result := Result / HistoryLength;
+end;
+
+{ calculates variance value of a history buffer }
+function Variance(History: TEnergyHistory; Average: single): single;
+ var I: integer;
+begin
+ Result := 0;
+
+ for I := 0 to HistoryLength - 1 do
+ Result := Result + sqr(History[I] - Average);
+
+ Result := Result / HistoryLength;
+end;
+
+{ shifts all values of the history to the right and
+ adds the new value at the front }
+procedure AddHistory(Value: single; var History: TEnergyHistory);
+ var I: integer;
+begin
+ for I := HistoryLength - 1 downto 1 do
+ History[I] := History[I-1];
+
+ History[0] := Value;
+end;
+
+{ calculates instant energy from FFT data for a specific
+ subchannel (0..SubChannelCount - 1) }
+function CalculateInstantEnergy(SubChannel: integer; Data: TFFTData): single;
+ var I: integer;
+begin
+ Result := 0;
+ for I := SubChannel * SamplesPerChannel to (SubChannel + 1) * SamplesPerChannel - 1 do
+ Result := Result + Data[I] * BeatEnergyModifier;
+
+ Result := Result / SamplesPerChannel;
+end;
+
+procedure TScreenCredits.DetectBeat;
+ var
+ Data: TFFTData;
+ I: integer;
+ Instant: single;
+ C, E, V: single;
+begin
+ AudioPlayback.GetFFTData(Data);
+
+ // do beatdetection for every subchannel
+ for I := 0 to SubChannelCount - 1 do
+ begin
+ Instant := CalculateInstantEnergy(I, Data);
+ E := Average(SubchannelHistory[I]);
+ V := Variance(SubchannelHistory[I], E);
+
+ C := (-0.0025714 * V) + 1.5142857;
+
+ AddHistory(Instant, SubChannelHistory[I]);
+
+ if (Instant > 2) and (Instant > C * E) then
+ begin
+ // beat detected
+ BeatDetected := true;
+ LastBeatTime := CTime;
+ end;
+ end;
+end;
+
+end.
diff --git a/src/screens/UScreenEdit.pas b/src/screens/UScreenEdit.pas
new file mode 100644
index 00000000..12e2948c
--- /dev/null
+++ b/src/screens/UScreenEdit.pas
@@ -0,0 +1,164 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenEdit;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UThemes;
+
+type
+ TScreenEdit = class(TMenu)
+ public
+ TextDescription: integer;
+ TextDescriptionLong: integer;
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure InteractNext; override;
+ procedure InteractPrev; override;
+ procedure InteractInc; override;
+ procedure InteractDec; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UMusic,
+ USkins,
+ UUnicodeUtils,
+ SysUtils;
+
+function TScreenEdit.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ SDL_ModState: word;
+begin
+ Result := true;
+
+ SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT +
+ KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT);
+
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenMain);
+ end;
+ SDLK_RETURN:
+ begin
+ if Interaction = 0 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenEditConvert);
+ end;
+
+ if Interaction = 1 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenMain);
+ end;
+ end;
+
+ SDLK_DOWN: InteractInc;
+ SDLK_UP: InteractDec;
+ SDLK_RIGHT: InteractNext;
+ SDLK_LEFT: InteractPrev;
+ end;
+ end;
+end;
+
+constructor TScreenEdit.Create;
+begin
+ inherited Create;
+
+ TextDescription := AddText(Theme.Edit.TextDescription);
+
+ LoadFromTheme(Theme.Edit);
+
+ AddButton(Theme.Edit.ButtonConvert);
+{ Some ideas for more:
+ AddButton(Theme.Edit.ButtonEditHeaders);
+ AddButton(Theme.Edit.ButtonAdjustGap);
+}
+ AddButton(Theme.Edit.ButtonExit);
+
+ Interaction := 0;
+end;
+
+procedure TScreenEdit.InteractNext;
+begin
+ inherited InteractNext;
+ Text[TextDescription].Text := Theme.Edit.Description[Interaction];
+end;
+
+procedure TScreenEdit.InteractPrev;
+begin
+ inherited InteractPrev;
+ Text[TextDescription].Text := Theme.Edit.Description[Interaction];
+end;
+
+procedure TScreenEdit.InteractDec;
+begin
+ inherited InteractDec;
+ Text[TextDescription].Text := Theme.Edit.Description[Interaction];
+end;
+
+procedure TScreenEdit.InteractInc;
+begin
+ inherited InteractInc;
+ Text[TextDescription].Text := Theme.Edit.Description[Interaction];
+end;
+
+procedure TScreenEdit.SetAnimationProgress(Progress: real);
+begin
+ Statics[0].Texture.ScaleW := Progress;
+ Statics[0].Texture.ScaleH := Progress;
+end;
+
+end.
diff --git a/src/screens/UScreenEditConvert.pas b/src/screens/UScreenEditConvert.pas
new file mode 100644
index 00000000..8b13d410
--- /dev/null
+++ b/src/screens/UScreenEditConvert.pas
@@ -0,0 +1,826 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenEditConvert;
+
+{*
+ * See
+ * MIDI Recommended Practice (RP-017): SMF Lyric Meta Event Definition
+ * http://www.midi.org/techspecs/rp17.php
+ * MIDI Recommended Practice (RP-026): SMF Language and Display Extensions
+ * http://www.midi.org/techspecs/rp26.php
+ * MIDI File Format
+ * http://www.sonicspot.com/guide/midifiles.html
+ * KMIDI File Format
+ * http://gnese.free.fr/Projects/KaraokeTime/Fichiers/karfaq.html
+ * http://journals.rpungin.fotki.com/karaoke/category/midi
+ *
+ * There are two widely spread karaoke formats:
+ * - KMIDI (.kar), an inofficial midi extension by Tune 1000
+ * - Standard Midi files with lyric meta-tags (SMF with lyrics, .mid).
+ *
+ * KMIDI uses two tracks, the first just contains a header (mostly track 2) and
+ * the second the lyrics (track 3). It uses text meta tags for the lyrics.
+ * SMF uses just one track (normally track 1) and uses lyric meta tags for storage.
+ *
+ * Most files are in the KMIDI format. Some Midi files contain both lyric types.
+ *}
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ math,
+ UMenu,
+ SDL,
+ {$IFDEF UseMIDIPort}
+ MidiFile,
+ MidiOut,
+ {$ENDIF}
+ ULog,
+ USongs,
+ USong,
+ UMusic,
+ UThemes,
+ UPath;
+
+type
+ TMidiNote = record
+ Event: integer;
+ EventType: integer;
+ Channel: integer;
+ Start: real;
+ Len: real;
+ Data1: integer;
+ Data2: integer;
+ Str: UTF8String; // normally ASCII
+ end;
+
+ TLyricType = (ltKMIDI, ltSMFLyric);
+
+ TTrack = record
+ Note: array of TMidiNote;
+ Name: UTF8String; // normally ASCII
+ Status: set of (tsNotes, tsLyrics); //< track contains notes, lyrics or both
+ LyricType: set of TLyricType;
+ NoteType: (ntNone, ntAvail);
+ end;
+
+ TNote = record
+ Start: integer;
+ Len: integer;
+ Tone: integer;
+ Lyric: UTF8String;
+ NewSentence: boolean;
+ end;
+
+ TArrayTrack = array of TTrack;
+
+ TScreenEditConvert = class(TMenu)
+ private
+ Tracks: TArrayTrack; // current track
+ ColR: array[0..100] of real;
+ ColG: array[0..100] of real;
+ ColB: array[0..100] of real;
+ Len: real;
+ SelTrack: integer; // index of selected track
+ fFileName: IPath;
+
+ {$IFDEF UseMIDIPort}
+ MidiFile: TMidiFile;
+ MidiOut: TMidiOutput;
+ {$ENDIF}
+
+ BPM: real;
+ Ticks: real;
+ Note: array of TNote;
+
+ procedure AddLyric(Start: integer; LyricType: TLyricType; Text: UTF8String);
+ procedure Extract(out Song: TSong; out Lines: TLines);
+
+ {$IFDEF UseMIDIPort}
+ procedure MidiFile1MidiEvent(event: PMidiEvent);
+ {$ENDIF}
+
+ function CountSelectedTracks: integer;
+
+ public
+ constructor Create; override;
+ procedure OnShow; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ function Draw: boolean; override;
+ procedure OnHide; override;
+ end;
+
+implementation
+
+uses
+ SysUtils,
+ TextGL,
+ gl,
+ UDrawTexture,
+ UFiles,
+ UGraphic,
+ UIni,
+ UMain,
+ UPathUtils,
+ USkins,
+ ULanguage,
+ UTextEncoding,
+ UUnicodeUtils;
+
+const
+ // MIDI/KAR lyrics are specified to be ASCII only.
+ // Assume backward compatible CP1252 encoding.
+ DEFAULT_ENCODING = encCP1252;
+
+const
+ MIDI_EVENTTYPE_NOTEOFF = $8;
+ MIDI_EVENTTYPE_NOTEON = $9;
+ MIDI_EVENTTYPE_META_SYSEX = $F;
+
+ MIDI_EVENT_META = $FF;
+ MIDI_META_TEXT = $1;
+ MIDI_META_LYRICS = $5;
+
+function TScreenEditConvert.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+{$IFDEF UseMIDIPort}
+var
+ SResult: TSaveSongResult;
+ Playing: boolean;
+ MidiTrack: TMidiTrack;
+ Song: TSong;
+ Lines: TLines;
+{$ENDIF}
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ {$IFDEF UseMIDIPort}
+ if (MidiFile <> nil) then
+ MidiFile.StopPlaying;
+ {$ENDIF}
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenEdit);
+ end;
+
+ SDLK_RETURN:
+ begin
+ if Interaction = 0 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ ScreenOpen.Filename := GamePath.Append('file.mid');
+ ScreenOpen.BackScreen := @ScreenEditConvert;
+ FadeTo(@ScreenOpen);
+ end
+ else if Interaction = 1 then
+ begin
+ {$IFDEF UseMIDIPort}
+ if (MidiFile <> nil) then
+ begin
+ MidiFile.OnMidiEvent := MidiFile1MidiEvent;
+ //MidiFile.GoToTime(MidiFile.GetTrackLength div 2);
+ MidiFile.StartPlaying;
+ end;
+ {$ENDIF}
+ end
+ else if Interaction = 2 then
+ begin
+ {$IFDEF UseMIDIPort}
+ if (MidiFile <> nil) then
+ begin
+ MidiFile.OnMidiEvent := nil;
+ MidiFile.StartPlaying;
+ end;
+ {$ENDIF}
+ end
+ else if Interaction = 3 then
+ begin
+ {$IFDEF UseMIDIPort}
+ if CountSelectedTracks > 0 then
+ begin
+ Extract(Song, Lines);
+ SResult := SaveSong(Song, Lines, fFileName.SetExtension('.txt'),
+ false);
+ FreeAndNil(Song);
+ if (SResult = ssrOK) then
+ ScreenPopupInfo.ShowPopup(Language.Translate('INFO_FILE_SAVED'))
+ else
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_SAVE_FILE_FAILED'));
+ end
+ else
+ begin
+ ScreenPopupError.ShowPopup(Language.Translate('EDITOR_ERROR_NO_TRACK_SELECTED'));
+ end;
+ {$ENDIF}
+ end;
+
+ end;
+
+ SDLK_SPACE:
+ begin
+ {$IFDEF UseMIDIPort}
+ if (MidiFile <> nil) then
+ begin
+ if (Tracks[SelTrack].NoteType = ntAvail) and
+ (Tracks[SelTrack].LyricType <> []) then
+ begin
+ if (Tracks[SelTrack].Status = []) then
+ Tracks[SelTrack].Status := [tsNotes]
+ else if (Tracks[SelTrack].Status = [tsNotes]) then
+ Tracks[SelTrack].Status := [tsLyrics]
+ else if (Tracks[SelTrack].Status = [tsLyrics]) then
+ Tracks[SelTrack].Status := [tsNotes, tsLyrics]
+ else if (Tracks[SelTrack].Status = [tsNotes, tsLyrics]) then
+ Tracks[SelTrack].Status := [];
+ end
+ else if (Tracks[SelTrack].NoteType = ntAvail) then
+ begin
+ if (Tracks[SelTrack].Status = []) then
+ Tracks[SelTrack].Status := [tsNotes]
+ else
+ Tracks[SelTrack].Status := [];
+ end
+ else if (Tracks[SelTrack].LyricType <> []) then
+ begin
+ if (Tracks[SelTrack].Status = []) then
+ Tracks[SelTrack].Status := [tsLyrics]
+ else
+ Tracks[SelTrack].Status := [];
+ end;
+
+ Playing := (MidiFile.GetCurrentTime > 0);
+ MidiFile.StopPlaying();
+ MidiTrack := MidiFile.GetTrack(SelTrack);
+ if tsNotes in Tracks[SelTrack].Status then
+ MidiTrack.OnMidiEvent := MidiFile1MidiEvent
+ else
+ MidiTrack.OnMidiEvent := nil;
+ if (Playing) then
+ MidiFile.ContinuePlaying();
+ end;
+ {$ENDIF}
+ end;
+
+ SDLK_RIGHT:
+ begin
+ InteractNext;
+ end;
+
+ SDLK_LEFT:
+ begin
+ InteractPrev;
+ end;
+
+ SDLK_DOWN:
+ begin
+ Inc(SelTrack);
+ if SelTrack > High(Tracks) then
+ SelTrack := 0;
+ end;
+ SDLK_UP:
+ begin
+ Dec(SelTrack);
+ if SelTrack < 0 then
+ SelTrack := High(Tracks);
+ end;
+ end;
+ end;
+end;
+
+procedure TScreenEditConvert.AddLyric(Start: integer; LyricType: TLyricType; Text: UTF8String);
+var
+ N: integer;
+begin
+ // find corresponding note
+ N := 0;
+ while (N <= High(Note)) do
+ begin
+ if Note[N].Start = Start then
+ Break;
+ Inc(N);
+ end;
+
+ // check if note was found
+ if (N > High(Note)) then
+ Exit;
+
+ // set text
+ if (LyricType = ltKMIDI) then
+ begin
+ // end of paragraph
+ if Copy(Text, 1, 1) = '\' then
+ begin
+ Delete(Text, 1, 1);
+ end
+ // end of line
+ else if Copy(Text, 1, 1) = '/' then
+ begin
+ Delete(Text, 1, 1);
+ Note[N].NewSentence := true;
+ end;
+ end
+ else // SMFLyric
+ begin
+ // Line Feed -> end of paragraph
+ if Copy(Text, 1, 1) = #$0A then
+ begin
+ Delete(Text, 1, 1);
+ end
+ // Carriage Return -> end of line
+ else if Copy(Text, 1, 1) = #$0D then
+ begin
+ Delete(Text, 1, 1);
+ Note[N].NewSentence := true;
+ end;
+ end;
+
+ // overwrite lyric or append
+ if Note[N].Lyric = '-' then
+ Note[N].Lyric := Text
+ else
+ Note[N].Lyric := Note[N].Lyric + Text;
+end;
+
+procedure TScreenEditConvert.Extract(out Song: TSong; out Lines: TLines);
+
+var
+ T: integer;
+ C: integer;
+ N: integer;
+ Nu: integer;
+ NoteTemp: TNote;
+ Move: integer;
+ Max, Min: integer;
+ LyricType: TLyricType;
+ Text: UTF8String;
+begin
+ // song info
+ Song := TSong.Create();
+ Song.Clear();
+ Song.Resolution := 4;
+ SetLength(Song.BPM, 1);
+ Song.BPM[0].BPM := BPM*4;
+ SetLength(Note, 0);
+
+ // extract notes
+ for T := 0 to High(Tracks) do
+ begin
+ if tsNotes in Tracks[T].Status then
+ begin
+ for N := 0 to High(Tracks[T].Note) do
+ begin
+ if (Tracks[T].Note[N].EventType = MIDI_EVENTTYPE_NOTEON) and
+ (Tracks[T].Note[N].Data2 > 0) then
+ begin
+ Nu := Length(Note);
+ SetLength(Note, Nu + 1);
+ Note[Nu].Start := Round(Tracks[T].Note[N].Start / Ticks);
+ Note[Nu].Len := Round(Tracks[T].Note[N].Len / Ticks);
+ Note[Nu].Tone := Tracks[T].Note[N].Data1 - 12*5;
+ Note[Nu].Lyric := '-';
+ end;
+ end;
+ end;
+ end;
+
+ // extract lyrics (and artist + title info)
+ for T := 0 to High(Tracks) do
+ begin
+ if not (tsLyrics in Tracks[T].Status) then
+ Continue;
+
+ for N := 0 to High(Tracks[T].Note) do
+ begin
+ if (Tracks[T].Note[N].Event = MIDI_EVENT_META) then
+ begin
+ // determine and validate lyric meta tag
+ if (ltKMIDI in Tracks[T].LyricType) and
+ (Tracks[T].Note[N].Data1 = MIDI_META_TEXT) then
+ begin
+ Text := Tracks[T].Note[N].Str;
+
+ // check for meta info
+ if (Length(Text) > 2) and (Text[1] = '@') then
+ begin
+ case Text[2] of
+ 'L': Song.Language := Copy(Text, 3, Length(Text)); // language
+ 'T': begin // title info
+ if (Song.Artist = '') then
+ Song.Artist := Copy(Text, 3, Length(Text))
+ else if (Song.Title = '') then
+ Song.Title := Copy(Text, 3, Length(Text));
+ end;
+ end;
+ Continue;
+ end;
+
+ LyricType := ltKMIDI;
+ end
+ else if (ltSMFLyric in Tracks[T].LyricType) and
+ (Tracks[T].Note[N].Data1 = MIDI_META_LYRICS) then
+ begin
+ LyricType := ltSMFLyric;
+ end
+ else
+ begin
+ // unknown meta event
+ Continue;
+ end;
+
+ AddLyric(Round(Tracks[T].Note[N].Start / Ticks), LyricType, Tracks[T].Note[N].Str);
+ end;
+ end;
+ end;
+
+ // sort notes
+ for N := 0 to High(Note) do
+ for Nu := 0 to High(Note)-1 do
+ if Note[Nu].Start > Note[Nu+1].Start then
+ begin
+ NoteTemp := Note[Nu];
+ Note[Nu] := Note[Nu+1];
+ Note[Nu+1] := NoteTemp;
+ end;
+
+ // move to 0 at beginning
+ Move := Note[0].Start;
+ for N := 0 to High(Note) do
+ Note[N].Start := Note[N].Start - Move;
+
+ // copy notes
+ SetLength(Lines.Line, 1);
+ Lines.Number := 1;
+ Lines.High := 0;
+ Lines.Current := 0;
+ Lines.Resolution := 0;
+ Lines.NotesGAP := 0;
+ Lines.ScoreValue := 0;
+
+ C := 0;
+ N := 0;
+ Lines.Line[C].HighNote := -1;
+
+ for Nu := 0 to High(Note) do
+ begin
+ if Note[Nu].NewSentence then // new line
+ begin
+ SetLength(Lines.Line, Length(Lines.Line)+1);
+ Lines.Number := Lines.Number + 1;
+ Lines.High := Lines.High + 1;
+ C := C + 1;
+ N := 0;
+ SetLength(Lines.Line[C].Note, 0);
+ Lines.Line[C].HighNote := -1;
+
+ //Calculate Start of the Last Sentence
+ if (C > 0) and (Nu > 0) then
+ begin
+ Max := Note[Nu].Start;
+ Min := Note[Nu-1].Start + Note[Nu-1].Len;
+
+ case (Max - Min) of
+ 0: Lines.Line[C].Start := Max;
+ 1: Lines.Line[C].Start := Max;
+ 2: Lines.Line[C].Start := Max - 1;
+ 3: Lines.Line[C].Start := Max - 2;
+ else
+ if ((Max - Min) > 4) then
+ Lines.Line[C].Start := Min + 2
+ else
+ Lines.Line[C].Start := Max;
+
+ end; // case
+
+ end;
+ end;
+
+ // create space for new note
+ SetLength(Lines.Line[C].Note, Length(Lines.Line[C].Note)+1);
+ Inc(Lines.Line[C].HighNote);
+
+ // initialize note
+ Lines.Line[C].Note[N].Start := Note[Nu].Start;
+ Lines.Line[C].Note[N].Length := Note[Nu].Len;
+ Lines.Line[C].Note[N].Tone := Note[Nu].Tone;
+ Lines.Line[C].Note[N].Text := DecodeStringUTF8(Note[Nu].Lyric, DEFAULT_ENCODING);
+ Lines.Line[C].Note[N].NoteType := ntNormal;
+ Inc(N);
+ end;
+end;
+
+function TScreenEditConvert.CountSelectedTracks: integer;
+var
+ T: integer; // track
+begin
+ Result := 0;
+ for T := 0 to High(Tracks) do
+ if tsNotes in Tracks[T].Status then
+ Inc(Result);
+end;
+
+{$IFDEF UseMIDIPort}
+procedure TScreenEditConvert.MidiFile1MidiEvent(event: PMidiEvent);
+begin
+ //Log.LogStatus(IntToStr(event.event), 'MIDI');
+ try
+ MidiOut.PutShort(event.event, event.data1, event.data2);
+ except
+ MidiFile.StopPlaying();
+ end;
+end;
+{$ENDIF}
+
+constructor TScreenEditConvert.Create;
+var
+ P: integer;
+begin
+ inherited Create;
+ AddButton(40, 20, 100, 40, Skin.GetTextureFileName('ButtonF'));
+ AddButtonText(15, 5, 0, 0, 0, 'Open');
+ //Button[High(Button)].Text[0].Size := 11;
+
+ AddButton(160, 20, 100, 40, Skin.GetTextureFileName('ButtonF'));
+ AddButtonText(25, 5, 0, 0, 0, 'Play');
+
+ AddButton(280, 20, 200, 40, Skin.GetTextureFileName('ButtonF'));
+ AddButtonText(25, 5, 0, 0, 0, 'Play Selected');
+
+ AddButton(500, 20, 100, 40, Skin.GetTextureFileName('ButtonF'));
+ AddButtonText(20, 5, 0, 0, 0, 'Save');
+
+ fFileName := PATH_NONE;
+
+ for P := 0 to 100 do
+ begin
+ ColR[P] := Random(10)/10;
+ ColG[P] := Random(10)/10;
+ ColB[P] := Random(10)/10;
+ end;
+
+end;
+
+procedure TScreenEditConvert.OnShow;
+{$IFDEF UseMIDIPort}
+var
+ T: integer; // track
+ N: integer; // note
+ MidiTrack: TMidiTrack;
+ MidiEvent: PMidiEvent;
+ FileOpened: boolean;
+ KMIDITrackIndex, SMFTrackIndex: integer;
+{$ENDIF}
+begin
+ inherited;
+
+ Interaction := 0;
+
+{$IFDEF UseMIDIPort}
+ MidiOut := TMidiOutput.Create(nil);
+ Log.LogInfo(MidiOut.ProductName, 'MIDI');
+ MidiOut.Open;
+ MidiFile := nil;
+ SetLength(Tracks, 0);
+
+ // Filename is only <> PATH_NONE if we called the OpenScreen before
+ fFilename := ScreenOpen.Filename;
+ if (fFilename = PATH_NONE) then
+ Exit;
+ ScreenOpen.Filename := PATH_NONE;
+
+ FileOpened := false;
+ if fFileName.Exists then
+ begin
+ MidiFile := TMidiFile.Create(nil);
+ MidiFile.Filename := fFileName;
+ try
+ MidiFile.ReadFile;
+ FileOpened := true;
+ except
+ MidiFile.Free;
+ end;
+ end;
+
+ if (not FileOpened) then
+ begin
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_FILE_NOT_FOUND'));
+ Exit;
+ end;
+
+ Len := 0;
+ SelTrack := 0;
+ BPM := MidiFile.Bpm;
+ Ticks := MidiFile.TicksPerQuarter / 4;
+
+ KMIDITrackIndex := -1;
+ SMFTrackIndex := -1;
+
+ SetLength(Tracks, MidiFile.NumberOfTracks);
+ for T := 0 to MidiFile.NumberOfTracks-1 do
+ Tracks[T].LyricType := [];
+
+ for T := 0 to MidiFile.NumberOfTracks-1 do
+ begin
+ MidiTrack := MidiFile.GetTrack(T);
+ MidiTrack.OnMidiEvent := nil;
+ Tracks[T].Name := DecodeStringUTF8(MidiTrack.getName, DEFAULT_ENCODING);
+ Tracks[T].NoteType := ntNone;
+ Tracks[T].Status := [];
+
+ SetLength(Tracks[T].Note, MidiTrack.getEventCount());
+ for N := 0 to MidiTrack.getEventCount-1 do
+ begin
+ MidiEvent := MidiTrack.GetEvent(N);
+
+ Tracks[T].Note[N].Start := MidiEvent.time;
+ Tracks[T].Note[N].Len := MidiEvent.len;
+ Tracks[T].Note[N].Event := MidiEvent.event;
+ Tracks[T].Note[N].EventType := MidiEvent.event shr 4;
+ Tracks[T].Note[N].Channel := MidiEvent.event and $0F;
+ Tracks[T].Note[N].Data1 := MidiEvent.data1;
+ Tracks[T].Note[N].Data2 := MidiEvent.data2;
+ Tracks[T].Note[N].Str := DecodeStringUTF8(MidiEvent.str, DEFAULT_ENCODING);
+
+ if (Tracks[T].Note[N].Event = MIDI_EVENT_META) then
+ begin
+ case (Tracks[T].Note[N].Data1) of
+ MIDI_META_TEXT: begin
+ // KMIDI lyrics (uses MIDI_META_TEXT events)
+ if (StrLComp(PAnsiChar(Tracks[T].Note[N].Str), '@KMIDI KARAOKE FILE', 19) = 0) and
+ (High(Tracks) >= T+1) then
+ begin
+ // The '@KMIDI ...' mark is in the first track (mostly named 'Soft Karaoke')
+ // but the lyrics are in the second track (named 'Words')
+ Tracks[T+1].LyricType := Tracks[T+1].LyricType + [ltKMIDI];
+ KMIDITrackIndex := T+1;
+ end;
+ end;
+ MIDI_META_LYRICS: begin
+ // lyrics in Standard Midi File format found (uses MIDI_META_LYRICS events)
+ Tracks[T].LyricType := Tracks[T].LyricType + [ltSMFLyric];
+ SMFTrackIndex := T;
+ end;
+ end;
+ end
+ else if (Tracks[T].Note[N].EventType = MIDI_EVENTTYPE_NOTEON) then
+ begin
+ // notes available
+ Tracks[T].NoteType := ntAvail;
+ end;
+
+ if Tracks[T].Note[N].Start + Tracks[T].Note[N].Len > Len then
+ Len := Tracks[T].Note[N].Start + Tracks[T].Note[N].Len;
+ end;
+ end;
+
+ // set default lyric track. Prefer KMIDI.
+ if (KMIDITrackIndex > -1) then
+ Tracks[KMIDITrackIndex].Status := Tracks[KMIDITrackIndex].Status + [tsLyrics]
+ else if (SMFTrackIndex > -1) then
+ Tracks[SMFTrackIndex].Status := Tracks[SMFTrackIndex].Status + [tsLyrics];
+{$ENDIF}
+end;
+
+function TScreenEditConvert.Draw: boolean;
+var
+ Count: integer;
+ Count2: integer;
+ Bottom: real;
+ X: real;
+ Y: real;
+ Height: real;
+ YSkip: real;
+begin
+ // draw static menu
+ inherited Draw;
+
+ Y := 100;
+
+ Height := min(480, 40 * Length(Tracks));
+ Bottom := Y + Height;
+
+ YSkip := Height / Length(Tracks);
+
+ // highlight selected track
+ DrawQuad(10, Y+SelTrack*YSkip, 780, YSkip, 0.8, 0.8, 0.8);
+
+ // track-selection info
+ for Count := 0 to High(Tracks) do
+ if Tracks[Count].Status <> [] then
+ DrawQuad(10, Y + Count*YSkip, 50, YSkip, 0.8, 0.3, 0.3);
+ glColor3f(0, 0, 0);
+ for Count := 0 to High(Tracks) do
+ begin
+ if Tracks[Count].NoteType = ntAvail then
+ begin
+ if tsNotes in Tracks[Count].Status then
+ glColor3f(0, 0, 0)
+ else
+ glColor3f(0.7, 0.7, 0.7);
+ SetFontPos(25, Y + Count*YSkip + 10);
+ SetFontSize(15);
+ glPrint('N');
+ end;
+ if Tracks[Count].LyricType <> [] then
+ begin
+ if tsLyrics in Tracks[Count].Status then
+ glColor3f(0, 0, 0)
+ else
+ glColor3f(0.7, 0.7, 0.7);
+ SetFontPos(40, Y + Count*YSkip + 10);
+ SetFontSize(15);
+ glPrint('L');
+ end;
+ end;
+
+ DrawLine( 10, Y, 10, Bottom, 0, 0, 0);
+ DrawLine( 60, Y, 60, Bottom, 0, 0, 0);
+ DrawLine(790, Y, 790, Bottom, 0, 0, 0);
+
+ for Count := 0 to Length(Tracks) do
+ DrawLine(10, Y + Count*YSkip, 790, Y + Count*YSkip, 0, 0, 0);
+
+ for Count := 0 to High(Tracks) do
+ begin
+ SetFontPos(65, Y + Count*YSkip);
+ SetFontSize(15);
+ glPrint(Tracks[Count].Name);
+ end;
+
+ for Count := 0 to High(Tracks) do
+ begin
+ for Count2 := 0 to High(Tracks[Count].Note) do
+ begin
+ if Tracks[Count].Note[Count2].EventType = MIDI_EVENTTYPE_NOTEON then
+ DrawQuad(60 + Tracks[Count].Note[Count2].Start/Len * 725,
+ Y + (Count+1)*YSkip - Tracks[Count].Note[Count2].Data1*35/127,
+ 3, 3,
+ ColR[Count], ColG[Count], ColB[Count]);
+ if Tracks[Count].Note[Count2].EventType = 15 then
+ DrawLine(60 + Tracks[Count].Note[Count2].Start/Len * 725, Y + 0.75 * YSkip + Count*YSkip,
+ 60 + Tracks[Count].Note[Count2].Start/Len * 725, Y + YSkip + Count*YSkip,
+ ColR[Count], ColG[Count], ColB[Count]);
+ end;
+ end;
+
+ // playing line
+ {$IFDEF UseMIDIPort}
+ if (MidiFile <> nil) then
+ X := 60 + MidiFile.GetCurrentTime/MidiFile.GetTrackLength*730;
+ {$ENDIF}
+ DrawLine(X, Y, X, Bottom, 0.3, 0.3, 0.3);
+
+ Result := true;
+end;
+
+procedure TScreenEditConvert.OnHide;
+begin
+{$IFDEF UseMIDIPort}
+ FreeAndNil(MidiFile);
+ MidiOut.Close;
+ FreeAndNil(MidiOut);
+{$ENDIF}
+end;
+
+end.
diff --git a/src/screens/UScreenEditHeader.pas b/src/screens/UScreenEditHeader.pas
new file mode 100644
index 00000000..1d697bc9
--- /dev/null
+++ b/src/screens/UScreenEditHeader.pas
@@ -0,0 +1,445 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenEditHeader;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ USongs,
+ USong,
+ UPath,
+ UThemes;
+
+type
+ TScreenEditHeader = class(TMenu)
+ public
+ CurrentSong: TSong;
+ TextTitle: integer;
+ TextArtist: integer;
+ TextMp3: integer;
+ TextBackground: integer;
+ TextVideo: integer;
+ TextVideoGAP: integer;
+ TextRelative: integer;
+ TextResolution: integer;
+ TextNotesGAP: integer;
+ TextStart: integer;
+ TextGAP: integer;
+ TextBPM: integer;
+ StaticTitle: integer;
+ StaticArtist: integer;
+ StaticMp3: integer;
+ StaticBackground: integer;
+ StaticVideo: integer;
+ StaticVideoGAP: integer;
+ StaticRelative: integer;
+ StaticResolution: integer;
+ StaticNotesGAP: integer;
+ StaticStart: integer;
+ StaticGAP: integer;
+ StaticBPM: integer;
+ Sel: array[0..11] of boolean;
+ procedure SetRoundButtons;
+
+ constructor Create; override;
+ procedure OnShow; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+{ function Draw: boolean; override;
+ procedure Finish;}
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UMusic,
+ SysUtils,
+ UFiles,
+ USkins,
+ UTexture,
+ UUnicodeUtils;
+
+function TScreenEditHeader.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ T: integer;
+begin
+ Result := true;
+ if (PressedDown) then // Key Down
+ begin // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE:
+ begin
+ //Music.PlayBack;
+ //FadeTo(@MainScreen);
+ Result := false;
+ end;
+
+ SDLK_RETURN:
+ begin
+ if Interaction = 1 then
+ begin
+ //Save;
+ end;
+ end;
+
+ SDLK_RIGHT:
+ begin
+ case Interaction of
+ 0..0: InteractNext;
+ 1: Interaction := 0;
+ end;
+ end;
+
+ SDLK_LEFT:
+ begin
+ case Interaction of
+ 0: Interaction := 1;
+ 1..1: InteractPrev;
+ end;
+ end;
+
+ SDLK_DOWN:
+ begin
+ case Interaction of
+ 0..1: Interaction := 2;
+ 2..12: InteractNext;
+ 13: Interaction := 0;
+ end;
+ end;
+
+ SDLK_UP:
+ begin
+ case Interaction of
+ 0..1: Interaction := 13;
+ 2: Interaction := 0;
+ 3..13: InteractPrev;
+ end;
+ end;
+
+ SDLK_BACKSPACE:
+ begin
+ T := Interaction - 2 + TextTitle;
+ if (Interaction >= 2) and (Interaction <= 13) and (Length(Text[T].Text) >= 1) then
+ begin
+ Text[T].DeleteLastLetter;
+ SetRoundButtons;
+ end;
+ end;
+
+ end;
+ case CharCode of
+ 32..255:
+ begin
+ if (Interaction >= 2) and (Interaction <= 13) then
+ begin
+ Text[Interaction - 2 + TextTitle].Text :=
+ Text[Interaction - 2 + TextTitle].Text + UCS4ToUTF8String(CharCode);
+ SetRoundButtons;
+ end;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenEditHeader.Create;
+begin
+ inherited Create;
+
+ AddButton(40, 20, 100, 40, Skin.GetTextureFileName('ButtonF'));
+ AddButtonText(15, 5, 'Open');
+
+ AddButton(160, 20, 100, 40, Skin.GetTextureFileName('ButtonF'));
+ AddButtonText(20, 5, 'Save');
+
+ AddBox(80, 60, 640, 550);
+
+ AddText(160, 110 + 0*30, 0, 30, 0, 0, 0, 'Title:');
+ AddText(160, 110 + 1*30, 0, 30, 0, 0, 0, 'Artist:');
+ AddText(160, 110 + 2*30, 0, 30, 0, 0, 0, 'MP3:');
+
+ AddText(160, 110 + 4*30, 0, 30, 0, 0, 0, 'Background:');
+ AddText(160, 110 + 5*30, 0, 30, 0, 0, 0, 'Video:');
+ AddText(160, 110 + 6*30, 0, 30, 0, 0, 0, 'VideoGAP:');
+
+ AddText(160, 110 + 8*30, 0, 30, 0, 0, 0, 'Relative:');
+ AddText(160, 110 + 9*30, 0, 30, 0, 0, 0, 'Resolution:');
+ AddText(160, 110 + 10*30, 0, 30, 0, 0, 0, 'NotesGAP:');
+
+ AddText(160, 110 + 12*30, 0, 30, 0, 0, 0, 'Start:');
+ AddText(160, 110 + 13*30, 0, 30, 0, 0, 0, 'GAP:');
+ AddText(160, 110 + 14*30, 0, 30, 0, 0, 0, 'BPM:');
+
+ TextTitle := AddText(340, 110 + 0*30, 0, 30, 0, 0, 0, '');
+ TextArtist := AddText(340, 110 + 1*30, 0, 30, 0, 0, 0, '');
+ TextMp3 := AddText(340, 110 + 2*30, 0, 30, 0, 0, 0, '');
+
+ TextBackground := AddText(340, 110 + 4*30, 0, 30, 0, 0, 0, '');
+ TextVideo := AddText(340, 110 + 5*30, 0, 30, 0, 0, 0, '');
+ TextVideoGAP := AddText(340, 110 + 6*30, 0, 30, 0, 0, 0, '');
+
+ TextRelative := AddText(340, 110 + 8*30, 0, 30, 0, 0, 0, '');
+ TextResolution := AddText(340, 110 + 9*30, 0, 30, 0, 0, 0, '');
+ TextNotesGAP := AddText(340, 110 + 10*30, 0, 30, 0, 0, 0, '');
+
+ TextStart := AddText(340, 110 + 12*30, 0, 30, 0, 0, 0, '');
+ TextGAP := AddText(340, 110 + 13*30, 0, 30, 0, 0, 0, '');
+ TextBPM := AddText(340, 110 + 14*30, 0, 30, 0, 0, 0, '');
+
+ StaticTitle := AddStatic(130, 115 + 0*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticArtist := AddStatic(130, 115 + 1*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticMp3 := AddStatic(130, 115 + 2*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticBackground := AddStatic(130, 115 + 4*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticVideo := AddStatic(130, 115 + 5*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticVideoGAP := AddStatic(130, 115 + 6*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticRelative := AddStatic(130, 115 + 8*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticResolution := AddStatic(130, 115 + 9*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticNotesGAP := AddStatic(130, 115 + 10*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticStart := AddStatic(130, 115 + 12*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticGAP := AddStatic(130, 115 + 13*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+ StaticBPM := AddStatic(130, 115 + 14*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF);
+
+ AddInteraction(iText, TextTitle);
+ AddInteraction(iText, TextArtist);
+ AddInteraction(iText, TextMp3);
+ AddInteraction(iText, TextBackground);
+ AddInteraction(iText, TextVideo);
+ AddInteraction(iText, TextVideoGAP);
+ AddInteraction(iText, TextRelative);
+ AddInteraction(iText, TextResolution);
+ AddInteraction(iText, TextNotesGAP);
+ AddInteraction(iText, TextStart);
+ AddInteraction(iText, TextGAP);
+ AddInteraction(iText, TextBPM);
+end;
+
+procedure TScreenEditHeader.OnShow;
+begin
+ inherited;
+
+{ if FileExists(FileName) then // load file
+ begin
+ CurrentSong.FileName := FileName;
+ SkanujPlik(CurrentSong);
+
+ SetLength(TrueBoolStrs, 1);
+ TrueBoolStrs[0] := 'yes';
+ SetLength(FalseBoolStrs, 1);
+ FalseBoolStrs[0] := 'no';
+
+ Text[TextTitle].Text := CurrentSong.Title;
+ Text[TextArtist].Text := CurrentSong.Artist;
+ Text[TextMP3].Text := CurrentSong.Mp3;
+ Text[TextBackground].Text := CurrentSong.Background;
+ Text[TextVideo].Text := CurrentSong.Video;
+ Text[TextVideoGAP].Text := FloatToStr(CurrentSong.VideoGAP);
+ Text[TextRelative].Text := BoolToStr(CurrentSong.Relative, true);
+ Text[TextResolution].Text := IntToStr(CurrentSong.Resolution);
+ Text[TextNotesGAP].Text := IntToStr(CurrentSong.NotesGAP);
+ Text[TextStart].Text := FloatToStr(CurrentSong.Start);
+ Text[TextGAP].Text := FloatToStr(CurrentSong.GAP);
+ Text[TextBPM].Text := FloatToStr(CurrentSong.BPM[0].BPM);
+ SetRoundButtons;
+ end;}
+
+ Interaction := 0;
+end;
+
+(*function TScreenEdit.Draw: boolean;
+var
+ Min: integer;
+ Sec: integer;
+ Count: integer;
+ AktBeat: integer;
+begin
+{ glClearColor(1,1,1,1);
+
+ // control music
+ if PlaySentence then
+ begin
+ // stop the music
+ if (Music.Position > PlayStopTime) then
+ begin
+ Music.Stop;
+ PlaySentence := false;
+ end;
+
+ // click
+ if (Click) and (PlaySentence) then
+ begin
+ AktBeat := Floor(CurrentSong.BPM[0].BPM * (Music.Position - CurrentSong.GAP / 1000) / 60);
+ Text[TextDebug].Text := IntToStr(AktBeat);
+ if AktBeat <> LastClick then
+ begin
+ for Count := 0 to Czesci[0].Czesc[Czesci[0].Akt].HighNut do
+ if (Czesci[0].Czesc[Czesci[0].Akt].Nuta[Count].Start = AktBeat) then
+ begin
+ Music.PlayClick;
+ LastClick := AktBeat;
+ end;
+ end;
+ end; // click
+ end; // if PlaySentence
+
+ Text[TextSentence].Text := IntToStr(Czesci[0].Akt + 1) + ' / ' + IntToStr(Czesci[0].Ilosc);
+ Text[TextNote].Text := IntToStr(AktNuta + 1) + ' / ' + IntToStr(Czesci[0].Czesc[Czesci[0].Akt].LengthNote);
+
+ // Song info
+ Text[TextBPM].Text := FloatToStr(CurrentSong.BPM[0].BPM / 4);
+ Text[TextGAP].Text := FloatToStr(CurrentSong.GAP);
+
+ // Note info
+ Text[TextNStart].Text := IntToStr(Czesci[0].Czesc[Czesci[0].Akt].Nuta[AktNuta].Start);
+ Text[TextNDlugosc].Text := IntToStr(Czesci[0].Czesc[Czesci[0].Akt].Nuta[AktNuta].Dlugosc);
+ Text[TextNTon].Text := IntToStr(Czesci[0].Czesc[Czesci[0].Akt].Nuta[AktNuta].Ton);
+ Text[TextNText].Text := Czesci[0].Czesc[Czesci[0].Akt].Nuta[AktNuta].Text;
+
+ // draw static menu
+ inherited Draw;
+
+ // draw notes
+ SingDrawNoteLines(20, 300, 780, 15);
+ SingDrawBeatDelimeters(40, 300, 760, 0);
+ SingDrawCzesc(40, 405, 760, 0);
+
+ // draw text
+ Lyric.Draw;}
+
+end;*)
+
+procedure TScreenEditHeader.SetRoundButtons;
+begin
+ if Length(Text[TextTitle].Text) > 0 then
+ Statics[StaticTitle].Visible := true
+ else
+ Statics[StaticTitle].Visible := false;
+
+ if Length(Text[TextArtist].Text) > 0 then
+ Statics[StaticArtist].Visible := true
+ else
+ Statics[StaticArtist].Visible := false;
+
+ if Length(Text[TextMp3].Text) > 0 then
+ Statics[StaticMp3].Visible := true
+ else
+ Statics[StaticMp3].Visible := false;
+
+ if Length(Text[TextBackground].Text) > 0 then
+ Statics[StaticBackground].Visible := true
+ else
+ Statics[StaticBackground].Visible := false;
+
+ if Length(Text[TextVideo].Text) > 0 then
+ Statics[StaticVideo].Visible := true
+ else
+ Statics[StaticVideo].Visible := false;
+
+ try
+ StrToFloat(Text[TextVideoGAP].Text);
+ if StrToFloat(Text[TextVideoGAP].Text)<> 0 then
+ Statics[StaticVideoGAP].Visible := true
+ else
+ Statics[StaticVideoGAP].Visible := false;
+ except
+ Statics[StaticVideoGAP].Visible := false;
+ end;
+
+ if LowerCase(Text[TextRelative].Text) = 'yes' then
+ Statics[StaticRelative].Visible := true
+ else
+ Statics[StaticRelative].Visible := false;
+
+ try
+ StrToInt(Text[TextResolution].Text);
+ if (StrToInt(Text[TextResolution].Text) <> 0) and (StrToInt(Text[TextResolution].Text) >= 1) then
+ Statics[StaticResolution].Visible := true
+ else
+ Statics[StaticResolution].Visible := false;
+ except
+ Statics[StaticResolution].Visible := false;
+ end;
+
+ try
+ StrToInt(Text[TextNotesGAP].Text);
+ Statics[StaticNotesGAP].Visible := true;
+ except
+ Statics[StaticNotesGAP].Visible := false;
+ end;
+
+ // start
+ try
+ StrToFloat(Text[TextStart].Text);
+ if (StrToFloat(Text[TextStart].Text) > 0) then
+ Statics[StaticStart].Visible := true
+ else
+ Statics[StaticStart].Visible := false;
+ except
+ Statics[StaticStart].Visible := false;
+ end;
+
+ // GAP
+ try
+ StrToFloat(Text[TextGAP].Text);
+ Statics[StaticGAP].Visible := true;
+ except
+ Statics[StaticGAP].Visible := false;
+ end;
+
+ // BPM
+ try
+ StrToFloat(Text[TextBPM].Text);
+ if (StrToFloat(Text[TextBPM].Text) > 0) then
+ Statics[StaticBPM].Visible := true
+ else
+ Statics[StaticBPM].Visible := false;
+ except
+ Statics[StaticBPM].Visible := false;
+ end;
+
+end;
+
+(*procedure TScreenEdit.Finish;
+begin
+//
+end;*)
+
+end.
diff --git a/src/screens/UScreenEditSub.pas b/src/screens/UScreenEditSub.pas
new file mode 100644
index 00000000..fb3f04ce
--- /dev/null
+++ b/src/screens/UScreenEditSub.pas
@@ -0,0 +1,1809 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenEditSub;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+{$I switches.inc}
+
+uses
+ UMenu,
+ UMusic,
+ SDL,
+ SysUtils,
+ UFiles,
+ UTime,
+ USongs,
+ USong,
+ UIni,
+ ULog,
+ UTexture,
+ UMenuText,
+ UEditorLyrics,
+ Math,
+ gl,
+ {$IFDEF UseMIDIPort}
+ MidiOut,
+ MidiCons,
+ {$ENDIF}
+ UThemes;
+
+type
+ TScreenEditSub = class(TMenu)
+ private
+ AktBeat: integer;
+ //Variable is True if no Song is loaded
+ Error: boolean;
+
+ TextNote: integer;
+ TextSentence: integer;
+ TextTitle: integer;
+ TextArtist: integer;
+ TextMp3: integer;
+ TextBPM: integer;
+ TextGAP: integer;
+ TextDebug: integer;
+ TextNStart: integer;
+ TextNLength: integer;
+ TextNTon: integer;
+ TextNText: integer;
+ CurrentNote: integer;
+ PlaySentence: boolean;
+ PlaySentenceMidi: boolean;
+ PlayStopTime: real;
+ LastClick: integer;
+ Click: boolean;
+ CopySrc: integer;
+
+ {$IFDEF UseMIDIPort}
+ MidiOut: TMidiOutput;
+ {$endif}
+
+ MidiStart: real;
+ MidiStop: real;
+ MidiTime: real;
+ MidiPos: real;
+ MidiLastNote: integer;
+
+ TextEditMode: boolean;
+ editText: UTF8String; //backup of current text in text-edit-mode
+
+ Lyric: TEditorLyrics;
+
+ procedure DivideBPM;
+ procedure MultiplyBPM;
+ procedure LyricsCapitalize;
+ procedure LyricsCorrectSpaces;
+ procedure FixTimings;
+ procedure DivideSentence;
+ procedure JoinSentence;
+ procedure DivideNote;
+ procedure DeleteNote;
+ procedure TransposeNote(Transpose: integer);
+ procedure ChangeWholeTone(Tone: integer);
+ procedure MoveAllToEnd(Move: integer);
+ procedure MoveTextToRight;
+ procedure MarkSrc;
+ procedure PasteText;
+ procedure CopySentence(Src, Dst: integer);
+ procedure CopySentences(Src, Dst, Num: integer);
+ procedure DrawStatics;
+ procedure DrawInfoBar(x, y, w, h: integer);
+ //Note Name Mod
+ function GetNoteName(Note: integer): string;
+ public
+ Tex_Background: TTexture;
+ FadeOut: boolean;
+ constructor Create; override;
+ procedure OnShow; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ function ParseInputEditText(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+ function Draw: boolean; override;
+ procedure OnHide; override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UDraw,
+ UNote,
+ USkins,
+ ULanguage,
+ UTextEncoding,
+ UUnicodeUtils,
+ UPath;
+
+
+procedure OnSaveEncodingError(Value: boolean; Data: Pointer);
+var
+ SResult: TSaveSongResult;
+ FilePath: IPath;
+ Success: boolean;
+begin
+ Success := false;
+ if (Value) then
+ begin
+ CurrentSong.Encoding := encUTF8;
+ FilePath := CurrentSong.Path.Append(CurrentSong.FileName);
+ // create backup file
+ FilePath.CopyFile(Path(FilePath.ToUTF8 + '.ansi.bak'), false);
+ // store in UTF-8 encoding
+ SResult := SaveSong(CurrentSong, Lines[0], FilePath,
+ boolean(Data));
+ Success := (SResult = ssrOK);
+ end;
+
+ if (Success) then
+ ScreenPopupInfo.ShowPopup(Language.Translate('INFO_FILE_SAVED'))
+ else
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_SAVE_FILE_FAILED'));
+end;
+
+// Method for input parsing. If false is returned, GetNextWindow
+// should be checked to know the next window to load;
+function TScreenEditSub.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ SDL_ModState: word;
+ R: real;
+ SResult: TSaveSongResult;
+begin
+ Result := true;
+
+ if TextEditMode then
+ begin
+ Result := ParseInputEditText(PressedKey, CharCode, PressedDown);
+ end
+ else
+ begin
+
+ SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT
+ + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT {+ KMOD_CAPS});
+
+ if (PressedDown) then // Key Down
+ begin
+ // check normal keys
+ case PressedKey of
+ SDLK_Q:
+ begin
+ Result := false;
+ Exit;
+ end;
+ SDLK_S:
+ begin
+ // Save Song
+ SResult := SaveSong(CurrentSong, Lines[0], CurrentSong.Path.Append(CurrentSong.FileName),
+ (SDL_ModState = KMOD_LSHIFT));
+ if (SResult = ssrOK) then
+ begin
+ //ScreenPopupInfo.ShowPopup(Language.Translate('INFO_FILE_SAVED'));
+ Text[TextDebug].Text := Language.Translate('INFO_FILE_SAVED');
+ end
+ else if (SResult = ssrEncodingError) then
+ begin
+ ScreenPopupCheck.ShowPopup(Language.Translate('ENCODING_ERROR_ASK_FOR_UTF8'), OnSaveEncodingError,
+ Pointer(SDL_ModState = KMOD_LSHIFT), true);
+ end
+ else
+ begin
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_SAVE_FILE_FAILED'));
+ end;
+ Exit;
+ end;
+
+ SDLK_R: //reload
+ begin
+ AudioPlayback.Stop;
+ {$IFDEF UseMIDIPort}
+ MidiOut.Close;
+ MidiOut.Free;
+ {$ENDIF}
+ Lyric.Free;
+
+ onShow;
+ Text[TextDebug].Text := 'song reloaded'; //TODO: Language.Translate('SONG_RELOADED');
+ end;
+
+ SDLK_D:
+ begin
+ // Divide lengths by 2
+ if (SDL_ModState = KMOD_LSHIFT) then
+ begin
+ DivideBPM;
+ Exit;
+ end;
+ end;
+ SDLK_M:
+ begin
+ // Multiply lengths by 2
+ if (SDL_ModState = KMOD_LSHIFT) then
+ begin
+ MultiplyBPM;
+ Exit;
+ end;
+ end;
+ SDLK_C:
+ begin
+ // Capitalize letter at the beginning of line
+ if SDL_ModState = 0 then
+ LyricsCapitalize;
+
+ // Correct spaces
+ if SDL_ModState = KMOD_LSHIFT then
+ LyricsCorrectSpaces;
+
+ // Copy sentence
+ if SDL_ModState = KMOD_LCTRL then
+ MarkSrc;
+
+ Exit;
+ end;
+ SDLK_V:
+ begin
+ // Paste text
+ if SDL_ModState = KMOD_LCTRL then
+ begin
+ if Lines[0].Line[Lines[0].Current].HighNote >= Lines[0].Line[CopySrc].HighNote then
+ PasteText
+ else
+ Log.LogStatus('PasteText: invalid range', 'TScreenEditSub.ParseInput');
+ end;
+
+ if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then
+ begin
+ CopySentence(CopySrc, Lines[0].Current);
+ end;
+ end;
+ SDLK_T:
+ begin
+ // Fixes timings between sentences
+ FixTimings;
+ Exit;
+ end;
+ SDLK_P:
+ begin
+ if SDL_ModState = 0 then
+ begin
+ // Play Sentence
+ Click := true;
+ AudioPlayback.Stop;
+ R := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[0].Start);
+ if R <= AudioPlayback.Length then
+ begin
+ AudioPlayback.Position := R;
+ PlayStopTime := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].End_);
+ PlaySentence := true;
+ AudioPlayback.Play;
+ LastClick := -100;
+ end;
+ end
+ else if SDL_ModState = KMOD_LSHIFT then
+ begin
+ PlaySentenceMidi := true;
+
+ MidiTime := USTime.GetTime;
+ MidiStart := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[0].Start);
+ MidiStop := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].End_);
+
+ LastClick := -100;
+ end
+ else if SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL then
+ begin
+ PlaySentenceMidi := true;
+ MidiTime := USTime.GetTime;
+ MidiStart := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[0].Start);
+ MidiStop := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].End_);
+ LastClick := -100;
+
+ PlaySentence := true;
+ Click := true;
+ AudioPlayback.Stop;
+ AudioPlayback.Position := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[0].Start)+0{-0.10};
+ PlayStopTime := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].End_)+0;
+ AudioPlayback.Play;
+ LastClick := -100;
+ end;
+ Exit;
+ end;
+
+ // Golden Note
+ SDLK_G:
+ begin
+ if (Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType = ntGolden) then
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntNormal
+ else
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntGolden;
+
+ Exit;
+ end;
+
+ // Freestyle Note
+ SDLK_F:
+ begin
+ if (Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType = ntFreestyle) then
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntNormal
+ else
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntFreestyle;
+
+ // update lyrics
+ Lyric.AddLine(Lines[0].Current);
+ Lyric.Selected := CurrentNote;
+
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ FadeTo(@ScreenSong);
+ end;
+
+ SDLK_BACKQUOTE:
+ begin
+ // Increase Note Length (same as Alt + Right)
+ Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length);
+ if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then
+ Inc(Lines[0].Line[Lines[0].Current].End_);
+ end;
+
+ SDLK_EQUALS:
+ begin
+ // Increase BPM
+ if SDL_ModState = 0 then
+ CurrentSong.BPM[0].BPM := Round((CurrentSong.BPM[0].BPM * 5) + 1) / 5; // (1/20)
+ if SDL_ModState = KMOD_LSHIFT then
+ CurrentSong.BPM[0].BPM := CurrentSong.BPM[0].BPM + 4; // (1/1)
+ if SDL_ModState = KMOD_LCTRL then
+ CurrentSong.BPM[0].BPM := Round((CurrentSong.BPM[0].BPM * 25) + 1) / 25; // (1/100)
+ end;
+
+ SDLK_MINUS:
+ begin
+ // Decrease BPM
+ if SDL_ModState = 0 then
+ CurrentSong.BPM[0].BPM := Round((CurrentSong.BPM[0].BPM * 5) - 1) / 5;
+ if SDL_ModState = KMOD_LSHIFT then
+ CurrentSong.BPM[0].BPM := CurrentSong.BPM[0].BPM - 4;
+ if SDL_ModState = KMOD_LCTRL then
+ CurrentSong.BPM[0].BPM := Round((CurrentSong.BPM[0].BPM * 25) - 1) / 25;
+ end;
+
+ SDLK_4:
+ begin
+ if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then
+ begin
+ CopySentence(CopySrc, Lines[0].Current);
+ CopySentence(CopySrc+1, Lines[0].Current+1);
+ CopySentence(CopySrc+2, Lines[0].Current+2);
+ CopySentence(CopySrc+3, Lines[0].Current+3);
+ end;
+
+ if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT + KMOD_LALT then
+ begin
+ CopySentences(CopySrc, Lines[0].Current, 4);
+ end;
+ end;
+ SDLK_5:
+ begin
+ if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then
+ begin
+ CopySentence(CopySrc, Lines[0].Current);
+ CopySentence(CopySrc+1, Lines[0].Current+1);
+ CopySentence(CopySrc+2, Lines[0].Current+2);
+ CopySentence(CopySrc+3, Lines[0].Current+3);
+ CopySentence(CopySrc+4, Lines[0].Current+4);
+ end;
+
+ if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT + KMOD_LALT then
+ begin
+ CopySentences(CopySrc, Lines[0].Current, 5);
+ end;
+ end;
+
+ SDLK_9:
+ begin
+ // Decrease GAP
+ if SDL_ModState = 0 then
+ CurrentSong.GAP := CurrentSong.GAP - 10;
+ if SDL_ModState = KMOD_LSHIFT then
+ CurrentSong.GAP := CurrentSong.GAP - 1000;
+ end;
+ SDLK_0:
+ begin
+ // Increase GAP
+ if SDL_ModState = 0 then
+ CurrentSong.GAP := CurrentSong.GAP + 10;
+ if SDL_ModState = KMOD_LSHIFT then
+ CurrentSong.GAP := CurrentSong.GAP + 1000;
+ end;
+
+ SDLK_KP_PLUS:
+ begin
+ // Increase tone of all notes
+ if SDL_ModState = 0 then
+ ChangeWholeTone(1);
+ if SDL_ModState = KMOD_LSHIFT then
+ ChangeWholeTone(12);
+ end;
+
+ SDLK_KP_MINUS:
+ begin
+ // Decrease tone of all notes
+ if SDL_ModState = 0 then
+ ChangeWholeTone(-1);
+ if SDL_ModState = KMOD_LSHIFT then
+ ChangeWholeTone(-12);
+ end;
+
+ SDLK_SLASH:
+ begin
+ if SDL_ModState = 0 then
+ begin
+ // Insert start of sentece
+ if CurrentNote > 0 then
+ DivideSentence;
+ end;
+
+ if SDL_ModState = KMOD_LSHIFT then
+ begin
+ // Join next sentence with current
+ if Lines[0].Current < Lines[0].High then
+ JoinSentence;
+ end;
+
+ if SDL_ModState = KMOD_LCTRL then
+ begin
+ // divide note
+ DivideNote;
+ end;
+
+ end;
+
+ SDLK_F4:
+ begin
+ // Enter Text Edit Mode
+ editText := Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text;
+ TextEditMode := true;
+ end;
+
+ SDLK_SPACE:
+ begin
+ if (SDL_ModState = 0) or (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) then
+ begin
+ // Play Sentence
+ PlaySentenceMidi := false; // stop midi
+ PlaySentence := true;
+ Click := false;
+ AudioPlayback.Stop;
+ AudioPlayback.Position := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start);
+ PlayStopTime := (GetTimeFromBeat(
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start +
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length));
+ AudioPlayback.Play;
+ LastClick := -100;
+ end;
+
+ if (SDL_ModState = KMOD_LSHIFT) or (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) then
+ begin
+ // Play Midi
+ PlaySentenceMidi := true;
+
+ MidiTime := USTime.GetTime;
+ MidiStart := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start);
+ MidiStop := GetTimeFromBeat(
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start +
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length);
+
+ LastClick := -100;
+ end;
+ end;
+
+ SDLK_RETURN:
+ begin
+ end;
+
+ SDLK_DELETE:
+ begin
+ if SDL_ModState = KMOD_LCTRL then
+ begin
+ // deletes current note
+ DeleteNote;
+ end;
+ end;
+
+ SDLK_PERIOD:
+ begin
+ // moves text to right in current sentence
+ MoveTextToRight;
+ end;
+
+ SDLK_RIGHT:
+ begin
+ // right
+ if SDL_ModState = 0 then
+ begin
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1;
+ Inc(CurrentNote);
+ if CurrentNote > Lines[0].Line[Lines[0].Current].HighNote then
+ CurrentNote := 0;
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2;
+ Lyric.Selected := CurrentNote;
+ end;
+
+ // ctrl + right
+ if (SDL_ModState = KMOD_LCTRL) or (SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT) then
+ begin
+ if Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length > 1 then
+ begin
+ Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length);
+ Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start);
+ if CurrentNote = 0 then
+ begin
+ Inc(Lines[0].Line[Lines[0].Current].Start);
+ end;
+ end;
+ end;
+
+ // shift + right
+ if SDL_ModState = KMOD_LSHIFT then
+ begin
+ Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start);
+ if CurrentNote = 0 then
+ begin
+ Inc(Lines[0].Line[Lines[0].Current].Start);
+ end;
+ if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then
+ Inc(Lines[0].Line[Lines[0].Current].End_);
+ end;
+
+ // alt + right
+ if (SDL_ModState = KMOD_LALT) or (SDL_ModState = KMOD_LALT + KMOD_LSHIFT) then
+ begin
+ Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length);
+ if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then
+ Inc(Lines[0].Line[Lines[0].Current].End_);
+ end;
+
+ // alt + ctrl + shift + right = move all from cursor to right
+ if SDL_ModState = KMOD_LALT + KMOD_LCTRL + KMOD_LSHIFT then
+ begin
+ MoveAllToEnd(1);
+ end;
+
+ end;
+
+ SDLK_LEFT:
+ begin
+ // left
+ if SDL_ModState = 0 then
+ begin
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1;
+ Dec(CurrentNote);
+ if CurrentNote = -1 then
+ CurrentNote := Lines[0].Line[Lines[0].Current].HighNote;
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2;
+ Lyric.Selected := CurrentNote;
+ end;
+
+ // ctrl + left
+ if (SDL_ModState = KMOD_LCTRL) or (SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT) then
+ begin
+ Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start);
+ Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length);
+ if CurrentNote = 0 then
+ begin
+ Dec(Lines[0].Line[Lines[0].Current].Start);
+ end;
+ end;
+
+ // shift + left
+ if SDL_ModState = KMOD_LSHIFT then
+ begin
+ Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start);
+
+ // resizing sentences
+ if CurrentNote = 0 then
+ begin
+ Dec(Lines[0].Line[Lines[0].Current].Start);
+ end;
+
+ if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then
+ Dec(Lines[0].Line[Lines[0].Current].End_);
+
+ end;
+
+ // alt + left
+ if (SDL_ModState = KMOD_LALT) or (SDL_ModState = KMOD_LALT + KMOD_LSHIFT) then
+ begin
+ if Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length > 1 then
+ begin
+ Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length);
+ if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then
+ Dec(Lines[0].Line[Lines[0].Current].End_);
+ end;
+ end;
+
+ // alt + ctrl + shift + right = move all from cursor to left
+ if SDL_ModState = KMOD_LALT + KMOD_LCTRL + KMOD_LSHIFT then
+ begin
+ MoveAllToEnd(-1);
+ end;
+
+ end;
+
+ SDLK_DOWN:
+ begin
+
+ // skip to next sentence
+ if SDL_ModState = 0 then
+ begin
+ {$IFDEF UseMIDIPort}
+ MidiOut.PutShort(MIDI_NOTEOFF or 1,
+ Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60,
+ 127);
+ PlaySentenceMidi := false;
+ {$ENDIF}
+
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1;
+ Inc(Lines[0].Current);
+ CurrentNote := 0;
+ if Lines[0].Current > Lines[0].High then
+ Lines[0].Current := 0;
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2;
+
+ Lyric.AddLine(Lines[0].Current);
+ Lyric.Selected := 0;
+ AudioPlayback.Stop;
+ PlaySentence := false;
+ end;
+
+ // decrease tone
+ if SDL_ModState = KMOD_LSHIFT then
+ begin
+ TransposeNote(-1);
+ end;
+
+ end;
+
+ SDLK_UP:
+ begin
+
+ // skip to previous sentence
+ if SDL_ModState = 0 then
+ begin
+ {$IFDEF UseMIDIPort}
+ MidiOut.PutShort(MIDI_NOTEOFF or 1,
+ Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60,
+ 127);
+ PlaySentenceMidi := false;
+ {$endif}
+
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1;
+ Dec(Lines[0].Current);
+ CurrentNote := 0;
+ if Lines[0].Current = -1 then
+ Lines[0].Current := Lines[0].High;
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2;
+
+ Lyric.AddLine(Lines[0].Current);
+ Lyric.Selected := 0;
+ AudioPlayback.Stop;
+ PlaySentence := false;
+ end;
+
+ // increase tone
+ if SDL_ModState = KMOD_LSHIFT then
+ begin
+ TransposeNote(1);
+ end;
+ end;
+
+ end; // case
+ end;
+ end; // if
+end;
+
+function TScreenEditSub.ParseInputEditText(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ SDL_ModState: word;
+begin
+ // used when in Text Edit Mode
+ Result := true;
+
+ SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT
+ + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT {+ KMOD_CAPS});
+
+ if (PressedDown) then
+ begin
+ // check normal keys
+ if (IsPrintableChar(CharCode)) then
+ begin
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text :=
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text + UCS4ToUTF8String(CharCode);
+
+ Lyric.AddLine(Lines[0].Current);
+ Lyric.Selected := CurrentNote;
+ Exit;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE:
+ begin
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text := editText;
+ Lyric.AddLine(Lines[0].Current);
+ Lyric.Selected := CurrentNote;
+ TextEditMode := false;
+ end;
+ SDLK_F4, SDLK_RETURN:
+ begin
+ // Exit Text Edit Mode
+ TextEditMode := false;
+ end;
+ SDLK_BACKSPACE:
+ begin
+ UTF8Delete(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text,
+ LengthUTF8(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text), 1);
+ Lyric.AddLine(Lines[0].Current);
+ Lyric.Selected := CurrentNote;
+ end;
+ SDLK_RIGHT:
+ begin
+ // right
+ if SDL_ModState = 0 then
+ begin
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1;
+ Inc(CurrentNote);
+ if CurrentNote > Lines[0].Line[Lines[0].Current].HighNote then
+ CurrentNote := 0;
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2;
+ Lyric.Selected := CurrentNote;
+ editText := Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ // left
+ if SDL_ModState = 0 then
+ begin
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1;
+ Dec(CurrentNote);
+ if CurrentNote = -1 then
+ CurrentNote := Lines[0].Line[Lines[0].Current].HighNote;
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2;
+ Lyric.Selected := CurrentNote;
+ editText := Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text;
+ end;
+ end;
+ end;
+ end;
+end;
+
+{
+procedure TScreenEditSub.NewBeat;
+begin
+ // click
+ for Pet := 0 to Lines[0].Line[Lines[0].Current].HighNut do
+ if (Lines[0].Line[Lines[0].Current].Note[Pet].Start = Czas.AktBeat) then
+ Music.PlayClick;
+end;
+}
+
+procedure TScreenEditSub.DivideBPM;
+var
+ C: integer;
+ N: integer;
+
+begin
+ CurrentSong.BPM[0].BPM := CurrentSong.BPM[0].BPM / 2;
+
+ for C := 0 to Lines[0].High do
+ begin
+ Lines[0].Line[C].Start := Lines[0].Line[C].Start div 2;
+ Lines[0].Line[C].End_ := Lines[0].Line[C].End_ div 2;
+ for N := 0 to Lines[0].Line[C].HighNote do
+ begin
+ Lines[0].Line[C].Note[N].Start := Lines[0].Line[C].Note[N].Start div 2;
+ Lines[0].Line[C].Note[N].Length := Round(Lines[0].Line[C].Note[N].Length / 2);
+ end; // N
+ end; // C
+end;
+
+procedure TScreenEditSub.MultiplyBPM;
+var
+ C: integer;
+ N: integer;
+begin
+ CurrentSong.BPM[0].BPM := CurrentSong.BPM[0].BPM * 2;
+ for C := 0 to Lines[0].High do
+ begin
+ Lines[0].Line[C].Start := Lines[0].Line[C].Start * 2;
+ Lines[0].Line[C].End_ := Lines[0].Line[C].End_ * 2;
+ for N := 0 to Lines[0].Line[C].HighNote do
+ begin
+ Lines[0].Line[C].Note[N].Start := Lines[0].Line[C].Note[N].Start * 2;
+ Lines[0].Line[C].Note[N].Length := Lines[0].Line[C].Note[N].Length * 2;
+ end; // N
+ end; // C
+end;
+
+procedure TScreenEditSub.LyricsCapitalize;
+var
+ C: integer;
+ //N: integer; // temporary
+ S: string;
+begin
+ // temporary
+ {
+ for C := 0 to Lines[0].High do
+ for N := 0 to Lines[0].Line[C].HighNut do
+ Lines[0].Line[C].Note[N].Text := UTF8LowerCase(Lines[0].Line[C].Note[N].Text);
+ }
+
+ for C := 0 to Lines[0].High do
+ begin
+ S := AnsiUpperCase(Copy(Lines[0].Line[C].Note[0].Text, 1, 1));
+ S := S + Copy(Lines[0].Line[C].Note[0].Text, 2, Length(Lines[0].Line[C].Note[0].Text)-1);
+ Lines[0].Line[C].Note[0].Text := S;
+ end; // C
+end;
+
+procedure TScreenEditSub.LyricsCorrectSpaces;
+var
+ C: integer;
+ N: integer;
+begin
+ for C := 0 to Lines[0].High do
+ begin
+ // correct starting spaces in the first word
+ while Copy(Lines[0].Line[C].Note[0].Text, 1, 1) = ' ' do
+ Lines[0].Line[C].Note[0].Text := Copy(Lines[0].Line[C].Note[0].Text, 2, 100);
+
+ // move spaces on the start to the end of the previous note
+ for N := 1 to Lines[0].Line[C].HighNote do
+ begin
+ while (Copy(Lines[0].Line[C].Note[N].Text, 1, 1) = ' ') do
+ begin
+ Lines[0].Line[C].Note[N].Text := Copy(Lines[0].Line[C].Note[N].Text, 2, 100);
+ Lines[0].Line[C].Note[N-1].Text := Lines[0].Line[C].Note[N-1].Text + ' ';
+ end;
+ end; // N
+
+ // correct '-' to '- '
+ for N := 0 to Lines[0].Line[C].HighNote do
+ begin
+ if Lines[0].Line[C].Note[N].Text = '-' then
+ Lines[0].Line[C].Note[N].Text := '- ';
+ end; // N
+
+ // add space to the previous note when the current word is '- '
+ for N := 1 to Lines[0].Line[C].HighNote do
+ begin
+ if Lines[0].Line[C].Note[N].Text = '- ' then
+ Lines[0].Line[C].Note[N-1].Text := Lines[0].Line[C].Note[N-1].Text + ' ';
+ end; // N
+
+ // correct too many spaces at the end of note
+ for N := 0 to Lines[0].Line[C].HighNote do
+ begin
+ while Copy(Lines[0].Line[C].Note[N].Text, Length(Lines[0].Line[C].Note[N].Text)-1, 2) = ' ' do
+ Lines[0].Line[C].Note[N].Text := Copy(Lines[0].Line[C].Note[N].Text, 1, Length(Lines[0].Line[C].Note[N].Text)-1);
+ end; // N
+
+ // and correct if there is no space at the end of sentence
+ N := Lines[0].Line[C].HighNote;
+ if Copy(Lines[0].Line[C].Note[N].Text, Length(Lines[0].Line[C].Note[N].Text), 1) <> ' ' then
+ Lines[0].Line[C].Note[N].Text := Lines[0].Line[C].Note[N].Text + ' ';
+
+ end; // C
+end;
+
+procedure TScreenEditSub.FixTimings;
+var
+ C: integer;
+ S: integer;
+ Min: integer;
+ Max: integer;
+begin
+ for C := 1 to Lines[0].High do
+ begin
+ with Lines[0].Line[C-1] do
+ begin
+ Min := Note[HighNote].Start + Note[HighNote].Length;
+ Max := Lines[0].Line[C].Note[0].Start;
+ case (Max - Min) of
+ 0: S := Max;
+ 1: S := Max;
+ 2: S := Max - 1;
+ 3: S := Max - 2;
+ else
+ if ((Max - Min) > 4) then
+ S := Min + 2
+ else
+ S := Max;
+ end; // case
+
+ Lines[0].Line[C].Start := S;
+ end; // with
+ end; // for
+end;
+
+procedure TScreenEditSub.DivideSentence;
+var
+ C: integer;
+ CStart: integer;
+ CNew: integer;
+ CLen: integer;
+ N: integer;
+ NStart: integer;
+ NHigh: integer;
+begin
+ // increase sentence length by 1
+ CLen := Length(Lines[0].Line);
+ SetLength(Lines[0].Line, CLen + 1);
+ Inc(Lines[0].Number);
+ Inc(Lines[0].High);
+
+ // move needed sentences to one forward. newly has the copy of divided sentence
+ CStart := Lines[0].Current;
+ for C := CLen-1 downto CStart do
+ Lines[0].Line[C+1] := Lines[0].Line[C];
+
+ // clear and set new sentence
+ CNew := CStart + 1;
+ NStart := CurrentNote;
+ Lines[0].Line[CNew].Start := Lines[0].Line[CStart].Note[NStart].Start;
+ Lines[0].Line[CNew].Lyric := '';
+ Lines[0].Line[CNew].End_ := 0;
+ Lines[0].Line[CNew].BaseNote := 0;//High(integer); // TODO: High (integer) will causes a memory exception later in this procedure. Weird!
+ Lines[0].Line[CNew].HighNote := -1;
+ SetLength(Lines[0].Line[CNew].Note, 0);
+
+ // move right notes to new sentences
+ NHigh := Lines[0].Line[CStart].HighNote;
+ for N := NStart to NHigh do
+ begin
+ // increase sentence counters
+ with Lines[0].Line[CNew] do
+ begin
+ Inc(HighNote);
+ SetLength(Note, HighNote + 1);
+ Note[HighNote] := Lines[0].Line[CStart].Note[N];
+ End_ := Note[HighNote].Start + Note[HighNote].Length;
+
+ if Note[HighNote].Tone < BaseNote then
+ BaseNote := Note[HighNote].Tone;
+ end;
+ end;
+
+ // clear old notes and set sentence counters
+ Lines[0].Line[CStart].HighNote := NStart - 1;
+ Lines[0].Line[CStart].End_ := Lines[0].Line[CStart].Note[NStart-1].Start +
+ Lines[0].Line[CStart].Note[NStart-1].Length;
+ SetLength(Lines[0].Line[CStart].Note, Lines[0].Line[CStart].HighNote + 1);
+
+ //recalculate BaseNote of the divided Sentence
+ with Lines[0].Line[CStart] do
+ begin
+ BaseNote := High(integer);
+
+ for N := 0 to HighNote do
+ if Note[N].Tone < BaseNote then
+ BaseNote := Note[N].Tone;
+ end;
+
+ Lines[0].Current := Lines[0].Current + 1;
+ CurrentNote := 0;
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2;
+ Lyric.AddLine(Lines[0].Current);
+end;
+
+procedure TScreenEditSub.JoinSentence;
+var
+ C: integer;
+ N: integer;
+ NStart: integer;
+ NDst: integer;
+begin
+ C := Lines[0].Current;
+
+ // set new sentence
+ NStart := Lines[0].Line[C].HighNote + 1;
+ Lines[0].Line[C].HighNote := Lines[0].Line[C].HighNote + Lines[0].Line[C+1].HighNote + 1;
+ SetLength(Lines[0].Line[C].Note, Lines[0].Line[C].HighNote + 1);
+
+ // move right notes to new sentences
+ for N := 0 to Lines[0].Line[C+1].HighNote do
+ begin
+ NDst := NStart + N;
+ Lines[0].Line[C].Note[NDst] := Lines[0].Line[C+1].Note[N];
+ end;
+
+ // increase sentence counters
+ NDst := Lines[0].Line[C].HighNote;
+ Lines[0].Line[C].End_ := Lines[0].Line[C].Note[NDst].Start +
+ Lines[0].Line[C].Note[NDst].Length;
+
+ // move needed sentences to one backward.
+ for C := Lines[0].Current + 1 to Lines[0].High - 1 do
+ Lines[0].Line[C] := Lines[0].Line[C+1];
+
+ // increase sentence length by 1
+ SetLength(Lines[0].Line, Length(Lines[0].Line) - 1);
+ Dec(Lines[0].Number);
+ Dec(Lines[0].High);
+end;
+
+procedure TScreenEditSub.DivideNote;
+var
+ C: integer;
+ N: integer;
+begin
+ C := Lines[0].Current;
+
+ with Lines[0].Line[C] do
+ begin
+ Inc(HighNote);
+ SetLength(Note, HighNote + 1);
+
+ // we copy all notes including selected one
+ for N := HighNote downto CurrentNote+1 do
+ begin
+ Note[N] := Note[N-1];
+ end;
+
+ // Note[Cur] and Note[Cur + 1] is identical at this point
+ // modify first note
+ Note[CurrentNote].Length := Note[CurrentNote+1].Length div 2 + Note[CurrentNote+1].Length mod 2;
+
+ // 2nd note
+ Note[CurrentNote+1].Start := Note[CurrentNote].Start + Note[CurrentNote].Length;
+ Note[CurrentNote+1].Length := Note[CurrentNote + 1].Length div 2;
+
+ Note[CurrentNote+1].Text := '~';
+ Note[CurrentNote+1].Color := 1;
+ end;
+
+ // update lyric display
+ Lyric.AddLine(Lines[0].Current);
+ Lyric.Selected := CurrentNote;
+end;
+
+procedure TScreenEditSub.DeleteNote;
+var
+ C: integer;
+ N: integer;
+begin
+ C := Lines[0].Current;
+
+ //Do Not delete Last Note
+ if (Lines[0].Line[C].HighNote > 0) then
+ begin
+ // we copy all notes from the next to the selected one
+ for N := CurrentNote+1 to Lines[0].Line[C].HighNote do
+ begin
+ Lines[0].Line[C].Note[N-1] := Lines[0].Line[C].Note[N];
+ end;
+
+ Dec(Lines[0].Line[C].HighNote);
+
+ SetLength(Lines[0].Line[C].Note, Lines[0].Line[C].HighNote + 1);
+
+ // last note was deleted
+ if (CurrentNote > Lines[0].Line[C].HighNote) then
+ begin
+ // select new last note
+ CurrentNote := Lines[0].Line[C].HighNote;
+
+ // correct Line ending
+ with Lines[0].Line[C] do
+ End_ := Note[HighNote].Start + Note[HighNote].Length;
+ end;
+
+ Lines[0].Line[C].Note[CurrentNote].Color := 2;
+ end
+ // Last Note of current Sentence Deleted - > Delete Sentence
+ // if there are more than two left
+ else if (Lines[0].High > 1) then
+ begin
+ //Move all Sentences after the current to the Left
+ for N := C+1 to Lines[0].High do
+ Lines[0].Line[N-1] := Lines[0].Line[N];
+
+ //Delete Last Sentence
+ SetLength(Lines[0].Line, Lines[0].High);
+ Lines[0].High := High(Lines[0].Line);
+ Lines[0].Number := Length(Lines[0].Line);
+
+ CurrentNote := 0;
+ if (C > 0) then
+ Lines[0].Current := C - 1
+ else
+ Lines[0].Current := 0;
+
+ Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2;
+ end;
+
+ // update lyric display
+ Lyric.AddLine(Lines[0].Current);
+ Lyric.Selected := CurrentNote;
+end;
+
+procedure TScreenEditSub.TransposeNote(Transpose: integer);
+begin
+ Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Tone, Transpose);
+end;
+
+procedure TScreenEditSub.ChangeWholeTone(Tone: integer);
+var
+ C: integer;
+ N: integer;
+begin
+ for C := 0 to Lines[0].High do
+ begin
+ Lines[0].Line[C].BaseNote := Lines[0].Line[C].BaseNote + Tone;
+ for N := 0 to Lines[0].Line[C].HighNote do
+ Lines[0].Line[C].Note[N].Tone := Lines[0].Line[C].Note[N].Tone + Tone;
+ end;
+end;
+
+procedure TScreenEditSub.MoveAllToEnd(Move: integer);
+var
+ C: integer;
+ N: integer;
+ NStart: integer;
+begin
+ for C := Lines[0].Current to Lines[0].High do
+ begin
+ NStart := 0;
+ if C = Lines[0].Current then
+ NStart := CurrentNote;
+ for N := NStart to Lines[0].Line[C].HighNote do
+ begin
+ Inc(Lines[0].Line[C].Note[N].Start, Move); // move note start
+
+ if N = 0 then
+ begin // fix beginning
+ Inc(Lines[0].Line[C].Start, Move);
+ end;
+
+ if N = Lines[0].Line[C].HighNote then // fix ending
+ Inc(Lines[0].Line[C].End_, Move);
+
+ end; // for
+ end; // for
+end;
+
+procedure TScreenEditSub.MoveTextToRight;
+var
+ C: integer;
+ N: integer;
+ NHigh: integer;
+begin
+ {
+ C := Lines[0].Current;
+
+ for N := Lines[0].Line[C].HighNut downto 1 do
+ begin
+ Lines[0].Line[C].Note[N].Text := Lines[0].Line[C].Note[N-1].Text;
+ end; // for
+
+ Lines[0].Line[C].Note[0].Text := '- ';
+ }
+
+ C := Lines[0].Current;
+ NHigh := Lines[0].Line[C].HighNote;
+
+ // last word
+ Lines[0].Line[C].Note[NHigh].Text := Lines[0].Line[C].Note[NHigh-1].Text + Lines[0].Line[C].Note[NHigh].Text;
+
+ // other words
+ for N := NHigh - 1 downto CurrentNote + 1 do
+ begin
+ Lines[0].Line[C].Note[N].Text := Lines[0].Line[C].Note[N-1].Text;
+ end; // for
+ Lines[0].Line[C].Note[CurrentNote].Text := '- ';
+end;
+
+procedure TScreenEditSub.MarkSrc;
+begin
+ CopySrc := Lines[0].Current;
+end;
+
+procedure TScreenEditSub.PasteText;
+var
+ C: integer;
+ N: integer;
+begin
+ C := Lines[0].Current;
+
+ for N := 0 to Lines[0].Line[CopySrc].HighNote do
+ Lines[0].Line[C].Note[N].Text := Lines[0].Line[CopySrc].Note[N].Text;
+end;
+
+procedure TScreenEditSub.CopySentence(Src, Dst: integer);
+var
+ N: integer;
+ Time1: integer;
+ Time2: integer;
+ TD: integer;
+begin
+ Time1 := Lines[0].Line[Src].Note[0].Start;
+ Time2 := Lines[0].Line[Dst].Note[0].Start;
+ TD := Time2-Time1;
+
+ SetLength(Lines[0].Line[Dst].Note, Lines[0].Line[Src].HighNote + 1);
+ Lines[0].Line[Dst].HighNote := Lines[0].Line[Src].HighNote;
+ for N := 0 to Lines[0].Line[Src].HighNote do
+ begin
+ Lines[0].Line[Dst].Note[N].Text := Lines[0].Line[Src].Note[N].Text;
+ Lines[0].Line[Dst].Note[N].Length := Lines[0].Line[Src].Note[N].Length;
+ Lines[0].Line[Dst].Note[N].Tone := Lines[0].Line[Src].Note[N].Tone;
+ Lines[0].Line[Dst].Note[N].Start := Lines[0].Line[Src].Note[N].Start + TD;
+ end;
+ N := Lines[0].Line[Src].HighNote;
+ Lines[0].Line[Dst].End_ := Lines[0].Line[Dst].Note[N].Start + Lines[0].Line[Dst].Note[N].Length;
+end;
+
+procedure TScreenEditSub.CopySentences(Src, Dst, Num: integer);
+var
+ C: integer;
+begin
+ // create place for new sentences
+ SetLength(Lines[0].Line, Lines[0].Number + Num - 1);
+
+ // moves sentences next to the destination
+ for C := Lines[0].High downto Dst + 1 do
+ begin
+ Lines[0].Line[C + Num - 1] := Lines[0].Line[C];
+ end;
+
+ // prepares new sentences: sets sentence start and create first note
+ for C := 1 to Num-1 do
+ begin
+ Lines[0].Line[Dst + C].Start := Lines[0].Line[Dst + C - 1].Note[0].Start +
+ (Lines[0].Line[Src + C].Note[0].Start - Lines[0].Line[Src + C - 1].Note[0].Start);
+ SetLength(Lines[0].Line[Dst + C].Note, 1);
+ Lines[0].Line[Dst + C].HighNote := 0;
+ Lines[0].Line[Dst + C].Note[0].Start := Lines[0].Line[Dst + C].Start;
+ Lines[0].Line[Dst + C].Note[0].Length := 1;
+ Lines[0].Line[Dst + C].End_ := Lines[0].Line[Dst + C].Start + 1;
+ end;
+
+ // increase counters
+ Lines[0].Number := Lines[0].Number + Num - 1;
+ Lines[0].High := Lines[0].High + Num - 1;
+
+ for C := 0 to Num-1 do
+ CopySentence(Src + C, Dst + C);
+end;
+
+procedure TScreenEditSub.DrawStatics;
+var
+ x, y, w, h: Integer;
+begin
+ //Theme:
+ //bg
+ glDisable(GL_BLEND);
+
+ x := 0;
+ y := 0;
+ w := 800;
+ h := 600;
+ glColor4f(0.3, 0.5, 0.6, 1);
+ glbegin(gl_quads);
+ glVertex2f(x, y);
+ glVertex2f(x, y+h);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x+w, y);
+ glEnd;
+
+ // Line
+ glColor4f(0.9, 0.9, 0.9, 1);
+ x := 20;
+ y := 5;
+ w := 200;
+ h := 40;
+ glbegin(gl_quads);
+ glVertex2f(x, y);
+ glVertex2f(x, y+h);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x+w, y);
+ glEnd;
+
+ // Note
+ x := 260;
+ y := 5;
+ w := 200;
+ h := 40;
+ glbegin(gl_quads);
+ glVertex2f(x, y);
+ glVertex2f(x, y+h);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x+w, y);
+ glEnd;
+
+ // some borders
+ x := 20;
+ y := 55;
+ w := 760;
+ h := 236;
+ glColor4f(0.9, 0.9, 0.9, 1);
+ glbegin(gl_quads);
+ glVertex2f(x, y);
+ glVertex2f(x, y+h);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x+w, y);
+ glEnd;
+
+ glColor4f(0, 0, 0, 1);
+ glLineWidth(2);
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(x-1, y-1);
+ glVertex2f(x+w+1, y-1);
+ glVertex2f(x+w+1, y+h+1);
+ glVertex2f(x-1, y+h+1);
+ glEnd;
+
+ x := 20;
+ y := 305;
+ w := 760;
+ h := 135;
+ glColor4f(0.9, 0.9, 0.9, 1);
+ glbegin(gl_quads);
+ glVertex2f(x, y);
+ glVertex2f(x, y+h);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x+w, y);
+ glEnd;
+
+ glColor4f(0, 0, 0, 1);
+ glLineWidth(2);
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(x-1, y-1);
+ glVertex2f(x+w+1, y-1);
+ glVertex2f(x+w+1, y+h+1);
+ glVertex2f(x-1, y+h+1);
+ glEnd;
+
+ x := 20;
+ y := 500;
+ w := 760;
+ h := 40;
+ glColor4f(0.9, 0.9, 0.9, 1);
+ glbegin(gl_quads);
+ glVertex2f(x, y);
+ glVertex2f(x, y+h);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x+w, y);
+ glEnd;
+
+ glColor4f(0, 0, 0, 1);
+ glLineWidth(2);
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(x-1, y-1);
+ glVertex2f(x+w+1, y-1);
+ glVertex2f(x+w+1, y+h+1);
+ glVertex2f(x-1, y+h+1);
+ glEnd;
+
+ glLineWidth(1);
+end;
+
+procedure TScreenEditSub.DrawInfoBar(x, y, w, h: integer);
+var
+ start, end_: integer;
+ SongStart, SongEnd: integer;
+ ww: integer;
+
+ pos: real;
+ br: real;
+
+ line, note: integer;
+ numLines, numNotes: integer;
+
+begin
+ numLines := Length(Lines[0].Line);
+
+ if(numLines=0) then
+ Exit;
+
+ SongStart := Lines[0].Line[0].Note[0].Start;
+ SongEnd := Lines[0].Line[numLines-1].End_;
+ ww := SongEnd - SongStart;
+
+ glColor4f(0, 0, 0, 1);
+ glDisable(GL_BLEND);
+ glLineWidth(2);
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(x-1, y-1);
+ glVertex2f(x+w+1, y-1);
+ glVertex2f(x+w+1, y+h+1);
+ glVertex2f(x-1, y+h+1);
+ glEnd;
+
+ glColor4f(0.9, 0.9, 0.9, 1);
+ glbegin(gl_quads);
+ glVertex2f(x, y);
+ glVertex2f(x, y+h);
+ glVertex2f(x+w, y+h);
+ glVertex2f(x+w, y);
+ glEnd;
+
+
+ for line := 0 to numLines - 1 do
+ begin
+ if (line = Lines[0].Current) and not (PlaySentence or PlaySentenceMidi) then
+ glColor4f(0.4, 0.4, 0, 1)
+ else
+ glColor4f(1, 0.6, 0, 1);
+
+
+ start := Lines[0].Line[line].Note[0].Start;
+ end_ := Lines[0].Line[line].Note[Lines[0].Line[line].HighNote].Start+
+ Lines[0].Line[line].Note[Lines[0].Line[line].HighNote].Length;
+
+ pos := (start - SongStart)/ww*w;
+ br := (end_-start)/ww*w;
+
+ glbegin(gl_quads);
+ glVertex2f(x+pos, y);
+ glVertex2f(x+pos, y+h);
+ glVertex2f(x+pos+br, y+h);
+ glVertex2f(x+pos+br, y);
+ glEnd;
+ end;
+
+ if(PlaySentence or PlaySentenceMidi) then
+ begin
+ glColor4f(0, 0, 0, 0.5);
+ pos := 0;
+ br := (AktBeat - SongStart)/ww*w;
+ if (br>w) then
+ br := w;
+ end else
+ begin
+ glColor4f(1, 0, 0, 1);
+ pos := (Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start - SongStart)/ww*w;
+ br := Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length/ww*w;
+ if (br<1) then
+ br := 1;
+ end;
+
+ glEnable(GL_BLEND);
+ glbegin(gl_quads);
+ glVertex2f(x+pos, y);
+ glVertex2f(x+pos, y+h);
+ glVertex2f(x+pos+br, y+h);
+ glVertex2f(x+pos+br, y);
+ glEnd;
+ glDisable(GL_BLEND);
+
+ glLineWidth(1);
+end;
+
+constructor TScreenEditSub.Create;
+begin
+ inherited Create;
+ SetLength(Player, 1);
+
+ // line
+ AddText(40, 11, 1, 30, 0, 0, 0, 'Line:');
+ TextSentence := AddText(110, 11, 1, 30, 0, 0, 0, '0 / 0');
+
+
+ // Note
+ AddText(282, 11, 1, 30, 0, 0, 0, 'Note:');
+ TextNote := AddText(360, 11, 1, 30, 0, 0, 0, '0 / 0');
+
+ // file info
+ AddText(30, 65, 0, 24, 0, 0, 0, 'Title:');
+ AddText(30, 90, 0, 24, 0, 0, 0, 'Artist:');
+ AddText(30, 115, 0, 24, 0, 0, 0, 'Mp3:');
+ AddText(30, 140, 0, 24, 0, 0, 0, 'BPM:');
+ AddText(30, 165, 0, 24, 0, 0, 0, 'GAP:');
+
+ TextTitle := AddText(180, 65, 0, 24, 0, 0, 0, 'a');
+ TextArtist := AddText(180, 90, 0, 24, 0, 0, 0, 'b');
+ TextMp3 := AddText(180, 115, 0, 24, 0, 0, 0, 'c');
+ TextBPM := AddText(180, 140, 0, 24, 0, 0, 0, 'd');
+ TextGAP := AddText(180, 165, 0, 24, 0, 0, 0, 'e');
+
+ // note info
+ AddText(30, 190, 0, 24, 0, 0, 0, 'Start:');
+ AddText(30, 215, 0, 24, 0, 0, 0, 'Duration:');
+ AddText(30, 240, 0, 24, 0, 0, 0, 'Tone:');
+ AddText(30, 265, 0, 24, 0, 0, 0, 'Text:'); //AddText(500, 265, 0, 8, 0, 0, 0, 'VideoGap:');
+
+ TextNStart := AddText(180, 190, 0, 24, 0, 0, 0, 'a');
+ TextNLength := AddText(180, 215, 0, 24, 0, 0, 0, 'b');
+ TextNTon := AddText(180, 240, 0, 24, 0, 0, 0, 'c');
+ TextNText := AddText(180, 265, 0, 24, 0, 0, 0, 'd');
+
+ //TextVideoGap := AddText(600, 265, 0, 24, 0, 0, 0, 'e');
+
+ // debug
+ TextDebug := AddText(30, 550, 0, 27, 0, 0, 0, '');
+
+end;
+
+procedure TScreenEditSub.OnShow;
+var
+ FileExt: IPath;
+begin
+ inherited;
+
+ AudioPlayback.Stop;
+ PlaySentence := false;
+ PlaySentenceMidi := false;
+
+ Log.LogStatus('Initializing', 'TEditScreen.OnShow');
+ Lyric := TEditorLyrics.Create;
+
+ ResetSingTemp;
+
+ try
+ //Check if File is XML
+ FileExt := CurrentSong.FileName.GetExtension;
+ if FileExt.ToUTF8 = '.xml' then
+ Error := not CurrentSong.LoadXMLSong()
+ else
+ begin
+ // reread header with custom tags
+ Error := not CurrentSong.Analyse(true);
+ if not Error then
+ Error := not CurrentSong.LoadSong;
+ end;
+ except
+ Error := true;
+ end;
+
+ if Error then
+ begin
+ //Error Loading Song -> Go back to Song Screen and Show some Error Message
+ FadeTo(@ScreenSong);
+ ScreenPopupError.ShowPopup (Language.Translate('ERROR_CORRUPT_SONG'));
+ Exit;
+ end
+ else
+ begin
+ {$IFDEF UseMIDIPort}
+ MidiOut := TMidiOutput.Create(nil);
+ MidiOut.Open;
+ {$ENDIF}
+ Text[TextTitle].Text := CurrentSong.Title;
+ Text[TextArtist].Text := CurrentSong.Artist;
+ Text[TextMp3].Text := CurrentSong.Mp3.ToUTF8;
+
+ Lines[0].Current := 0;
+ CurrentNote := 0;
+ Lines[0].Line[0].Note[0].Color := 2;
+ AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3));
+ //Set Down Music Volume for Better hearability of Midi Sounds
+ //Music.SetVolume(0.4);
+
+ Lyric.Clear;
+ Lyric.X := 400;
+ Lyric.Y := 500;
+ Lyric.Align := atCenter;
+ Lyric.Size := 42;
+ Lyric.ColR := 0;
+ Lyric.ColG := 0;
+ Lyric.ColB := 0;
+ Lyric.ColSR := Skin_FontHighlightR;
+ Lyric.ColSG := Skin_FontHighlightG;
+ Lyric.ColSB := Skin_FontHighlightB;
+ Lyric.AddLine(0);
+ Lyric.Selected := 0;
+
+ NotesH := 7;
+ NotesW := 4;
+
+ end;
+
+ //Interaction := 0;
+ TextEditMode := false;
+end;
+
+function TScreenEditSub.Draw: boolean;
+var
+ Pet: integer;
+begin
+
+ glClearColor(1,1,1,1);
+
+ // midi music
+ if PlaySentenceMidi then
+ begin
+ {$IFDEF UseMIDIPort}
+ MidiPos := USTime.GetTime - MidiTime + MidiStart;
+
+ // stop the music
+ if (MidiPos > MidiStop) then
+ begin
+ MidiOut.PutShort(MIDI_NOTEOFF or 1,
+ Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60,
+ 127);
+ PlaySentenceMidi := false;
+ end;
+ {$ENDIF}
+
+ // click
+ AktBeat := Floor(GetMidBeat(MidiPos - CurrentSong.GAP / 1000));
+ Text[TextDebug].Text := IntToStr(AktBeat);
+
+ if AktBeat <> LastClick then
+ begin
+ for Pet := 0 to Lines[0].Line[Lines[0].Current].HighNote do
+ if (Lines[0].Line[Lines[0].Current].Note[Pet].Start = AktBeat) then
+ begin
+
+ LastClick := AktBeat;
+ {$IFDEF UseMIDIPort}
+ if Pet > 0 then
+ begin
+ MidiOut.PutShort(MIDI_NOTEOFF or 1,
+ Lines[0].Line[Lines[0].Current].Note[Pet-1].Tone + 60,
+ 127);
+ end;
+ MidiOut.PutShort(MIDI_NOTEON or 1,
+ Lines[0].Line[Lines[0].Current].Note[Pet].Tone + 60,
+ 127);
+ MidiLastNote := Pet;
+ {$ENDIF}
+
+ end;
+ end;
+ end; // if PlaySentenceMidi
+
+ // mp3 music
+ if PlaySentence then
+ begin
+ // stop the music
+ if (AudioPlayback.Position > PlayStopTime) then
+ begin
+ AudioPlayback.Stop;
+ PlaySentence := false;
+ end;
+
+ // click
+ if (Click) and (PlaySentence) then
+ begin
+ //AktBeat := Floor(CurrentSong.BPM[0].BPM * (Music.Position - CurrentSong.GAP / 1000) / 60);
+ AktBeat := Floor(GetMidBeat(AudioPlayback.Position - CurrentSong.GAP / 1000));
+ Text[TextDebug].Text := IntToStr(AktBeat);
+ if AktBeat <> LastClick then
+ begin
+ for Pet := 0 to Lines[0].Line[Lines[0].Current].HighNote do
+ if (Lines[0].Line[Lines[0].Current].Note[Pet].Start = AktBeat) then
+ begin
+ AudioPlayback.PlaySound( SoundLib.Click );
+ LastClick := AktBeat;
+ end;
+ end;
+ end; // click
+ end; // if PlaySentence
+
+
+ Text[TextSentence].Text := IntToStr(Lines[0].Current + 1) + ' / ' + IntToStr(Lines[0].Number);
+ Text[TextNote].Text := IntToStr(CurrentNote + 1) + ' / ' + IntToStr(Lines[0].Line[Lines[0].Current].HighNote + 1);
+
+ // Song info
+ Text[TextBPM].Text := FloatToStr(CurrentSong.BPM[0].BPM / 4);
+ Text[TextGAP].Text := FloatToStr(CurrentSong.GAP);
+
+ //Error reading Variables when no Song is loaded
+ if not Error then
+ begin
+ // Note info
+ Text[TextNStart].Text := IntToStr(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start);
+ Text[TextNLength].Text := IntToStr(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length);
+ Text[TextNTon].Text := IntToStr(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Tone) + ' ( ' + GetNoteName(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Tone) + ' )';
+ Text[TextNText].Text := Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text;
+ end;
+
+ // Text Edit Mode
+ if TextEditMode then
+ Text[TextNText].Text := Text[TextNText].Text + '|';
+
+ // draw static menu
+ DrawStatics;
+ DrawInfoBar(20, 460, 760, 20);
+ //inherited Draw;
+ DrawFG;
+ // draw notes
+ SingDrawNoteLines(20, 305, 780, 15);
+ //Error Drawing when no Song is loaded
+ if not Error then
+ begin
+ SingDrawBeatDelimeters(40, 305, 760, 0);
+ EditDrawLine(40, 410, 760, 0, 15);
+ end;
+
+ // draw text
+ Lyric.Draw;
+
+ Result := true;
+end;
+
+procedure TScreenEditSub.OnHide;
+begin
+ {$IFDEF UseMIDIPort}
+ MidiOut.Close;
+ MidiOut.Free;
+ {$ENDIF}
+ Lyric.Free;
+ //Music.SetVolume(1.0);
+end;
+
+function TScreenEditSub.GetNoteName(Note: integer): string;
+var
+ N1, N2: integer;
+begin
+ if (Note > 0) then
+ begin
+ N1 := Note mod 12;
+ N2 := Note div 12;
+ end
+ else
+ begin
+ N1 := (Note + (-Trunc(Note/12)+1)*12) mod 12;
+ N2 := -1;
+ end;
+
+ case N1 of
+ 0: Result := 'c';
+ 1: Result := 'c#';
+ 2: Result := 'd';
+ 3: Result := 'd#';
+ 4: Result := 'e';
+ 5: Result := 'f';
+ 6: Result := 'f#';
+ 7: Result := 'g';
+ 8: Result := 'g#';
+ 9: Result := 'a';
+ 10: Result := 'b';
+ 11: Result := 'h';
+ end;
+
+ case N2 of
+ 0: Result := UpperCase(Result); //Normal Uppercase Note, 1: Normal lowercase Note
+ 2: Result := Result + ''''; //One Striped
+ 3: Result := Result + ''''''; //Two Striped
+ 4: Result := Result + ''''''''; //etc.
+ 5: Result := Result + '''''''''';
+ 6: Result := Result + '''''''''''';
+ 7: Result := Result + '''''''''''''';
+ end;
+end;
+
+end. \ No newline at end of file
diff --git a/src/screens/UScreenLevel.pas b/src/screens/UScreenLevel.pas
new file mode 100644
index 00000000..1ead9773
--- /dev/null
+++ b/src/screens/UScreenLevel.pas
@@ -0,0 +1,139 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenLevel;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UDisplay,
+ UMusic,
+ UFiles,
+ SysUtils,
+ UThemes;
+
+type
+ TScreenLevel = class(TMenu)
+ public
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UIni,
+ USong,
+ UTexture,
+ UUnicodeUtils;
+
+function TScreenLevel.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+
+ if Ini.OnSongClick = sSelectPlayer then
+ FadeTo(@ScreenMain)
+ else
+ FadeTo(@ScreenName);
+ end;
+
+ SDLK_RETURN:
+ begin
+ Ini.Difficulty := Interaction;
+ Ini.SaveLevel;
+ AudioPlayback.PlaySound(SoundLib.Start);
+ //Set Standard Mode
+ ScreenSong.Mode := smNormal;
+ FadeTo(@ScreenSong);
+ end;
+
+ // Up and Down could be done at the same time,
+ // but I don't want to declare variables inside
+ // functions like this one, called so many times
+ SDLK_DOWN: InteractNext;
+ SDLK_UP: InteractPrev;
+ SDLK_RIGHT: InteractNext;
+ SDLK_LEFT: InteractPrev;
+ end;
+ end;
+end;
+
+constructor TScreenLevel.Create;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.Level);
+
+ AddButton(Theme.Level.ButtonEasy);
+ AddButton(Theme.Level.ButtonMedium);
+ AddButton(Theme.Level.ButtonHard);
+
+ Interaction := 0;
+end;
+
+procedure TScreenLevel.OnShow;
+begin
+ inherited;
+
+ Interaction := Ini.Difficulty;
+end;
+
+procedure TScreenLevel.SetAnimationProgress(Progress: real);
+begin
+ Button[0].Texture.ScaleW := Progress;
+ Button[1].Texture.ScaleW := Progress;
+ Button[2].Texture.ScaleW := Progress;
+end;
+
+end.
diff --git a/src/screens/UScreenLoading.pas b/src/screens/UScreenLoading.pas
new file mode 100644
index 00000000..e368f181
--- /dev/null
+++ b/src/screens/UScreenLoading.pas
@@ -0,0 +1,78 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenLoading;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ SysUtils,
+ UThemes,
+ gl;
+
+type
+ TScreenLoading = class(TMenu)
+ public
+ Fadeout: boolean;
+
+ constructor Create; override;
+ procedure OnShow; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UTime;
+
+function TScreenLoading.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+end;
+
+constructor TScreenLoading.Create;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.Loading);
+
+ Fadeout := false;
+end;
+
+procedure TScreenLoading.OnShow;
+begin
+ inherited;
+end;
+
+end.
diff --git a/src/screens/UScreenMain.pas b/src/screens/UScreenMain.pas
new file mode 100644
index 00000000..aa313cf6
--- /dev/null
+++ b/src/screens/UScreenMain.pas
@@ -0,0 +1,296 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenMain;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UDisplay,
+ UMusic,
+ UFiles,
+ SysUtils,
+ UThemes;
+
+type
+ TScreenMain = class(TMenu)
+ private
+ { ticks when the user interacted, used to start credits
+ after a period of time w/o user interaction }
+ UserInteractionTicks: cardinal;
+ public
+ TextDescription: integer;
+ TextDescriptionLong: integer;
+
+ constructor Create; override;
+ function ParseInput(PressedKey: Cardinal; CharCode: UCS4Char;
+ PressedDown: boolean): boolean; override;
+ function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; override;
+ procedure OnShow; override;
+ procedure SetInteraction(Num: integer); override;
+ procedure SetAnimationProgress(Progress: real); override;
+ function Draw: boolean; override;
+ end;
+
+const
+ { start credits after 60 seconds w/o interaction }
+ TicksUntilCredits = 60 * 1000;
+
+implementation
+
+uses
+ UGraphic,
+ UNote,
+ UIni,
+ UTexture,
+ USongs,
+ Textgl,
+ ULanguage,
+ UParty,
+ UScreenCredits,
+ USkins,
+ UUnicodeUtils;
+
+function TScreenMain.ParseInput(PressedKey: Cardinal; CharCode: UCS4Char;
+ PressedDown: boolean): boolean;
+var
+ SDL_ModState: word;
+begin
+ Result := true;
+
+ { reset user interaction timer }
+ UserInteractionTicks := SDL_GetTicks;
+
+ SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT +
+ KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT);
+
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'): begin
+ Result := false;
+ Exit;
+ end;
+ Ord('C'): begin
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ FadeTo(@ScreenCredits, SoundLib.Start);
+ Exit;
+ end;
+ end;
+ Ord('M'): begin
+ if (Ini.Players >= 1) and (Party.ModesAvailable) then
+ begin
+ FadeTo(@ScreenPartyOptions, SoundLib.Start);
+ Exit;
+ end;
+ end;
+
+ Ord('S'): begin
+ FadeTo(@ScreenStatMain, SoundLib.Start);
+ Exit;
+ end;
+
+ Ord('E'): begin
+ FadeTo(@ScreenEdit, SoundLib.Start);
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE:
+ begin
+ Result := false;
+ end;
+
+ SDLK_RETURN:
+ begin
+ //Solo
+ if (Interaction = 0) then
+ 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;
+
+ if Ini.OnSongClick = sSelectPlayer then
+ FadeTo(@ScreenLevel)
+ else
+ begin
+ ScreenName.Goto_SingScreen := false;
+ FadeTo(@ScreenName, SoundLib.Start);
+ end;
+ end
+ else //show error message
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_SONGS'));
+ end;
+
+ //Multi
+ if Interaction = 1 then
+ begin
+ if (Songs.SongList.Count >= 1) then
+ begin
+ FadeTo(@ScreenPartyOptions, SoundLib.Start);
+ end
+ else //show error message, No Songs Loaded
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_SONGS'));
+ end;
+
+ //Stats
+ if Interaction = 2 then
+ begin
+ FadeTo(@ScreenStatMain, SoundLib.Start);
+ end;
+
+ //Editor
+ if Interaction = 3 then
+ begin
+ {$IFDEF UseMIDIPort}
+ FadeTo(@ScreenEdit, SoundLib.Start);
+ {$ELSE}
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_EDITOR'));
+ {$ENDIF}
+ end;
+
+ //Options
+ if Interaction = 4 then
+ begin
+ FadeTo(@ScreenOptions, SoundLib.Start);
+ end;
+
+ //Exit
+ if Interaction = 5 then
+ begin
+ Result := false;
+ end;
+ end;
+ {**
+ * Up and Down could be done at the same time,
+ * but I don't want to declare variables inside
+ * functions like this one, called so many times
+ *}
+ SDLK_DOWN: InteractInc;
+ SDLK_UP: InteractDec;
+ SDLK_RIGHT: InteractNext;
+ SDLK_LEFT: InteractPrev;
+ end;
+ end
+ else // Key Up
+ case PressedKey of
+ SDLK_RETURN:
+ begin
+ end;
+ end;
+end;
+
+function TScreenMain.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean;
+begin
+ // default mouse behaviour
+ Result := inherited ParseMouse(MouseButton, BtnDown, X, Y);
+
+ { reset user interaction timer }
+ UserInteractionTicks := SDL_GetTicks;
+end;
+
+constructor TScreenMain.Create;
+begin
+ inherited Create;
+{**
+ * Attention ^^:
+ * New Creation Order needed because of LoadFromTheme
+ * and Button Collections.
+ * At First Custom Texts and Statics
+ * Then LoadFromTheme
+ * after LoadFromTheme the Buttons and Selects
+ *}
+ TextDescription := AddText(Theme.Main.TextDescription);
+ TextDescriptionLong := AddText(Theme.Main.TextDescriptionLong);
+
+ LoadFromTheme(Theme.Main);
+
+ AddButton(Theme.Main.ButtonSolo);
+ AddButton(Theme.Main.ButtonMulti);
+ AddButton(Theme.Main.ButtonStat);
+ AddButton(Theme.Main.ButtonEditor);
+ AddButton(Theme.Main.ButtonOptions);
+ AddButton(Theme.Main.ButtonExit);
+
+ Interaction := 0;
+end;
+
+procedure TScreenMain.OnShow;
+begin
+ inherited;
+
+ SoundLib.StartBgMusic;
+
+ {**
+ * Clean up TPartyGame here
+ * at the moment there is no better place for this
+ *}
+ Party.Clear;
+
+ { reset user interaction timer }
+ UserInteractionTicks := SDL_GetTicks;
+end;
+
+function TScreenMain.Draw: boolean;
+begin
+ Result := inherited Draw;
+
+ { start credits after a period w/o user interaction }
+ if (UserInteractionTicks + TicksUntilCredits < SDL_GetTicks) then
+ begin
+ FadeTo(@ScreenCredits, SoundLib.Start);
+ end;
+end;
+
+procedure TScreenMain.SetInteraction(Num: integer);
+begin
+ inherited SetInteraction(Num);
+ Text[TextDescription].Text := Theme.Main.Description[Interaction];
+ Text[TextDescriptionLong].Text := Theme.Main.DescriptionLong[Interaction];
+end;
+
+procedure TScreenMain.SetAnimationProgress(Progress: real);
+begin
+ Statics[0].Texture.ScaleW := Progress;
+ Statics[0].Texture.ScaleH := Progress;
+end;
+
+end.
diff --git a/src/screens/UScreenName.pas b/src/screens/UScreenName.pas
new file mode 100644
index 00000000..42af50d7
--- /dev/null
+++ b/src/screens/UScreenName.pas
@@ -0,0 +1,284 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenName;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SysUtils,
+ SDL,
+ UDisplay,
+ UFiles,
+ UMenu,
+ UMusic,
+ UThemes;
+
+type
+ TScreenName = class(TMenu)
+ public
+ Goto_SingScreen: boolean; //If true then next Screen in SingScreen
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+implementation
+
+uses
+ UCommon,
+ UGraphic,
+ UIni,
+ UNote,
+ UTexture,
+ UUnicodeUtils;
+
+
+function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ I: integer;
+ SDL_ModState: word;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+
+ SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT
+ + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT);
+
+ // check normal keys
+ if (IsPrintableChar(CharCode)) then
+ begin
+ Button[Interaction].Text[0].Text := Button[Interaction].Text[0].Text +
+ UCS4ToUTF8String(CharCode);
+ Exit;
+ end;
+
+ // check special keys
+ case PressedKey of
+ // Templates for Names Mod
+ SDLK_F1:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[0] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[0];
+ end;
+ SDLK_F2:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[1] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[1];
+ end;
+ SDLK_F3:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[2] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[2];
+ end;
+ SDLK_F4:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[3] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[3];
+ end;
+ SDLK_F5:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[4] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[4];
+ end;
+ SDLK_F6:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[5] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[5];
+ end;
+ SDLK_F7:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[6] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[6];
+ end;
+ SDLK_F8:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[7] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[7];
+ end;
+ SDLK_F9:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[8] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[8];
+ end;
+ SDLK_F10:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[9] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[9];
+ end;
+ SDLK_F11:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[10] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[10];
+ end;
+ SDLK_F12:
+ if (SDL_ModState = KMOD_LALT) then
+ begin
+ Ini.NameTemplate[11] := Button[Interaction].Text[0].Text;
+ end
+ else
+ begin
+ Button[Interaction].Text[0].Text := Ini.NameTemplate[11];
+ end;
+
+ SDLK_BACKSPACE:
+ begin
+ Button[Interaction].Text[0].DeleteLastLetter();
+ end;
+
+ SDLK_ESCAPE :
+ begin
+ Ini.SaveNames;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ if GoTo_SingScreen then
+ FadeTo(@ScreenSong)
+ else
+ FadeTo(@ScreenMain);
+ end;
+
+ SDLK_RETURN:
+ begin
+ for I := 1 to 6 do
+ Ini.Name[I-1] := Button[I-1].Text[0].Text;
+ Ini.SaveNames;
+ AudioPlayback.PlaySound(SoundLib.Start);
+
+ if GoTo_SingScreen then
+ FadeTo(@ScreenSing)
+ else
+ FadeTo(@ScreenLevel);
+
+ GoTo_SingScreen := false;
+ end;
+
+ // Up and Down could be done at the same time,
+ // but I don't want to declare variables inside
+ // functions like this one, called so many times
+ SDLK_DOWN: InteractNext;
+ SDLK_UP: InteractPrev;
+ SDLK_RIGHT: InteractNext;
+ SDLK_LEFT: InteractPrev;
+ end;
+ end;
+end;
+
+constructor TScreenName.Create;
+var
+ I: integer;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.Name);
+
+ for I := 1 to 6 do
+ AddButton(Theme.Name.ButtonPlayer[I]);
+
+ Interaction := 0;
+end;
+
+procedure TScreenName.OnShow;
+var
+ I: integer;
+begin
+ inherited;
+
+ for I := 1 to 6 do
+ Button[I-1].Text[0].Text := Ini.Name[I-1];
+
+ for I := 1 to PlayersPlay do
+ begin
+ Button[I-1].Visible := true;
+ Button[I-1].Selectable := true;
+ end;
+
+ for I := PlayersPlay+1 to 6 do
+ begin
+ Button[I-1].Visible := false;
+ Button[I-1].Selectable := false;
+ end;
+
+end;
+
+procedure TScreenName.SetAnimationProgress(Progress: real);
+var
+ I: integer;
+begin
+ for I := 1 to 6 do
+ Button[I-1].Texture.ScaleW := Progress;
+end;
+
+end.
diff --git a/src/screens/UScreenOpen.pas b/src/screens/UScreenOpen.pas
new file mode 100644
index 00000000..70b883c4
--- /dev/null
+++ b/src/screens/UScreenOpen.pas
@@ -0,0 +1,231 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOpen;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ Math,
+ SysUtils,
+ gl,
+ SDL,
+ UPath,
+ UMenu,
+ UMusic,
+ UFiles,
+ UTime,
+ USongs,
+ UIni,
+ ULog,
+ UTexture,
+ UMenuText,
+ ULyrics,
+ UThemes;
+
+type
+ TScreenOpen = class(TMenu)
+ private
+ //fTextF: array[0..1] of integer;
+ fTextN: integer; // text-box ID of filename
+ fFilename: IPath;
+ fBackScreen: PMenu;
+
+ procedure AddBox(X, Y, W, H: real);
+ public
+ constructor Create; override;
+ procedure OnShow; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+
+ {**
+ * Set by the caller to provide a default filename.
+ * Set to the selected filename after calling this screen or to PATH_NONE
+ * if the screen was aborted.
+ * TODO: maybe pass this value with a callback OnValueChanged()
+ *}
+ property Filename: IPath READ fFilename WRITE fFilename;
+ {** The screen that is shown after this screen is closed (set by the caller) *}
+ property BackScreen: PMenu READ fBackScreen WRITE fBackScreen;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UDraw,
+ UMain,
+ UScreenEditConvert,
+ USkins,
+ UUnicodeUtils;
+
+function TScreenOpen.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+
+ if (PressedDown) then // Key Down
+ begin
+ // check normal keys
+ if (IsPrintableChar(CharCode)) then
+ begin
+ if (Interaction = 0) then
+ begin
+ Text[fTextN].Text := Text[fTextN].Text + UCS4ToUTF8String(CharCode);
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_BACKSPACE: // del
+ begin
+ if Interaction = 0 then
+ begin
+ Text[fTextN].DeleteLastLetter;
+ end;
+ end;
+
+ SDLK_ESCAPE:
+ begin
+ //Empty Filename and go to last Screen
+ fFileName := PATH_NONE;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(fBackScreen);
+ end;
+
+ SDLK_RETURN:
+ begin
+ if (Interaction = 2) then
+ begin
+ //Update Filename and go to last Screen
+ fFileName := Path(Text[fTextN].Text);
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(fBackScreen);
+ end
+ else if (Interaction = 1) then
+ begin
+ //Empty Filename and go to last Screen
+ fFileName := PATH_NONE;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(fBackScreen);
+ end;
+ end;
+
+ SDLK_LEFT:
+ begin
+ InteractPrev;
+ end;
+
+ SDLK_RIGHT:
+ begin
+ InteractNext;
+ end;
+
+ SDLK_DOWN:
+ begin
+ end;
+
+ SDLK_UP:
+ begin
+ end;
+ end;
+ end;
+end;
+
+procedure TScreenOpen.AddBox(X, Y, W, H: real);
+begin
+ AddStatic(X, Y, W, H, 0, 0, 0, Skin.GetTextureFileName('MainBar'), TEXTURE_TYPE_COLORIZED);
+ AddStatic(X+2, Y+2, W-4, H-4, 1, 1, 1, Skin.GetTextureFileName('MainBar'), TEXTURE_TYPE_COLORIZED);
+end;
+
+constructor TScreenOpen.Create;
+begin
+ inherited Create;
+
+ fFilename := PATH_NONE;
+
+ // line
+ {
+ AddStatic(20, 10, 80, 30, 0, 0, 0, 'MainBar', 'JPG', TEXTURE_TYPE_COLORIZED);
+ AddText(35, 17, 1, 18, 1, 1, 1, 'line');
+ TextSentence := AddText(120, 14, 1, 24, 0, 0, 0, '0 / 0');
+ }
+
+ // file list
+ //AddBox(400, 100, 350, 450);
+
+ //TextF[0] := AddText(430, 155, 0, 24, 0, 0, 0, 'a');
+ //TextF[1] := AddText(430, 180, 0, 24, 0, 0, 0, 'a');
+
+ // file name
+ AddBox(20, 540, 500, 40);
+ fTextN := AddText(50, 548, 0, 24, 0, 0, 0, fFileName.ToUTF8);
+ AddInteraction(iText, fTextN);
+
+ // buttons
+ {AddButton(540, 540, 100, 40, Skin.SkinPath + Skin.ButtonF);
+ AddButtonText(10, 5, 0, 0, 0, 'Cancel');
+
+ AddButton(670, 540, 100, 40, Skin.SkinPath + Skin.ButtonF);
+ AddButtonText(30, 5, 0, 0, 0, 'OK');}
+ // buttons
+ AddButton(540, 540, 100, 40, Skin.GetTextureFileName('ButtonF'));
+ AddButtonText(10, 5, 0, 0, 0, 'Cancel');
+
+ AddButton(670, 540, 100, 40, Skin.GetTextureFileName('ButtonF'));
+ AddButtonText(30, 5, 0, 0, 0, 'OK');
+
+end;
+
+procedure TScreenOpen.OnShow;
+begin
+ inherited;
+
+ Interaction := 0;
+ Text[fTextN].Text := fFilename.ToUTF8();
+end;
+
+(*
+function TScreenEditSub.Draw: boolean;
+var
+ Min: integer;
+ Sec: integer;
+ AktBeat: integer;
+begin
+
+end;
+
+procedure TScreenEditSub.Finish;
+begin
+//
+end;
+*)
+
+end.
diff --git a/src/screens/UScreenOptions.pas b/src/screens/UScreenOptions.pas
new file mode 100644
index 00000000..30e3e9c4
--- /dev/null
+++ b/src/screens/UScreenOptions.pas
@@ -0,0 +1,234 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOptions;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ SysUtils,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UFiles,
+ UIni,
+ UThemes;
+
+type
+ TScreenOptions = class(TMenu)
+ public
+ TextDescription: integer;
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure InteractNext; override;
+ procedure InteractPrev; override;
+ procedure InteractNextRow; override;
+ procedure InteractPrevRow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UUnicodeUtils;
+
+function TScreenOptions.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenMain);
+ end;
+ SDLK_RETURN:
+ begin
+ if SelInteraction = 0 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenOptionsGame);
+ end;
+
+ if SelInteraction = 1 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenOptionsGraphics);
+ end;
+
+ if SelInteraction = 2 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenOptionsSound);
+ end;
+
+ if SelInteraction = 3 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenOptionsLyrics);
+ end;
+
+ if SelInteraction = 4 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenOptionsThemes);
+ end;
+
+ if SelInteraction = 5 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenOptionsRecord);
+ end;
+
+ if SelInteraction = 6 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenOptionsAdvanced);
+ end;
+
+ if SelInteraction = 7 then
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenMain);
+ end;
+ end;
+ SDLK_DOWN: InteractNextRow;
+ SDLK_UP: InteractPrevRow;
+ SDLK_RIGHT: InteractNext;
+ SDLK_LEFT: InteractPrev;
+ end;
+ end;
+end;
+
+constructor TScreenOptions.Create;
+begin
+ inherited Create;
+
+ TextDescription := AddText(Theme.Options.TextDescription);
+
+ LoadFromTheme(Theme.Options);
+
+ AddButton(Theme.Options.ButtonGame);
+ if (Length(Button[0].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[0]);
+
+ AddButton(Theme.Options.ButtonGraphics);
+ if (Length(Button[1].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[1]);
+
+ AddButton(Theme.Options.ButtonSound);
+ if (Length(Button[2].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[2]);
+
+ AddButton(Theme.Options.ButtonLyrics);
+ if (Length(Button[3].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[3]);
+
+ AddButton(Theme.Options.ButtonThemes);
+ if (Length(Button[4].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[4]);
+
+ AddButton(Theme.Options.ButtonRecord);
+ if (Length(Button[5].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[5]);
+
+ AddButton(Theme.Options.ButtonAdvanced);
+ if (Length(Button[6].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[6]);
+
+ AddButton(Theme.Options.ButtonExit);
+ if (Length(Button[7].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[7]);
+
+ Interaction := 0;
+end;
+
+procedure TScreenOptions.OnShow;
+begin
+ inherited;
+ // continue possibly stopped bg-music (stopped in record options)
+ SoundLib.StartBgMusic;
+end;
+
+procedure TScreenOptions.InteractNext;
+begin
+ inherited InteractNext;
+ Text[TextDescription].Text := Theme.Options.Description[Interaction];
+end;
+
+procedure TScreenOptions.InteractPrev;
+begin
+ inherited InteractPrev;
+ Text[TextDescription].Text := Theme.Options.Description[Interaction];
+end;
+
+procedure TScreenOptions.InteractNextRow;
+begin
+ inherited InteractNextRow;
+ Text[TextDescription].Text := Theme.Options.Description[Interaction];
+end;
+
+procedure TScreenOptions.InteractPrevRow;
+begin
+ inherited InteractPrevRow;
+ Text[TextDescription].Text := Theme.Options.Description[Interaction];
+end;
+
+procedure TScreenOptions.SetAnimationProgress(Progress: real);
+begin
+ Button[0].Texture.ScaleW := Progress;
+ Button[1].Texture.ScaleW := Progress;
+ Button[2].Texture.ScaleW := Progress;
+ Button[3].Texture.ScaleW := Progress;
+ Button[4].Texture.ScaleW := Progress;
+ Button[5].Texture.ScaleW := Progress;
+ Button[6].Texture.ScaleW := Progress;
+ Button[7].Texture.ScaleW := Progress;
+end;
+
+end.
diff --git a/src/screens/UScreenOptionsAdvanced.pas b/src/screens/UScreenOptionsAdvanced.pas
new file mode 100644
index 00000000..dd727dd8
--- /dev/null
+++ b/src/screens/UScreenOptionsAdvanced.pas
@@ -0,0 +1,168 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOptionsAdvanced;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UFiles,
+ UIni,
+ UThemes;
+
+type
+ TScreenOptionsAdvanced = class(TMenu)
+ public
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UUnicodeUtils,
+ SysUtils;
+
+function TScreenOptionsAdvanced.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ SDLK_RETURN:
+ begin
+ //SelectLoadAnimation Hidden because it is useless atm
+ //if SelInteraction = 7 then begin
+ if SelInteraction = 6 then
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ end;
+ SDLK_DOWN:
+ InteractNext;
+ SDLK_UP :
+ InteractPrev;
+ SDLK_RIGHT:
+ begin
+ //SelectLoadAnimation Hidden because it is useless atm
+ //if (SelInteraction >= 0) and (SelInteraction <= 6) then begin
+ if (SelInteraction >= 0) and (SelInteraction <= 5) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ //SelectLoadAnimation Hidden because it is useless atm
+ //if (SelInteraction >= 0) and (SelInteraction <= 6) then begin
+ if (SelInteraction >= 0) and (SelInteraction <= 5) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+ end;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenOptionsAdvanced.Create;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.OptionsAdvanced);
+
+ //SelectLoadAnimation Hidden because it is useless atm
+ //AddSelect(Theme.OptionsAdvanced.SelectLoadAnimation, Ini.LoadAnimation, ILoadAnimationTranslated);
+ Theme.OptionsAdvanced.SelectScreenFade.showArrows := true;
+ Theme.OptionsAdvanced.SelectScreenFade.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsAdvanced.SelectScreenFade, Ini.ScreenFade, IScreenFadeTranslated);
+
+ Theme.OptionsAdvanced.SelectEffectSing.showArrows := true;
+ Theme.OptionsAdvanced.SelectEffectSing.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsAdvanced.SelectEffectSing, Ini.EffectSing, IEffectSingTranslated);
+
+ Theme.OptionsAdvanced.SelectLineBonus.showArrows := true;
+ Theme.OptionsAdvanced.SelectLineBonus.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsAdvanced.SelectLineBonus, Ini.LineBonus, ILineBonusTranslated);
+
+ Theme.OptionsAdvanced.SelectOnSongClick.showArrows := true;
+ Theme.OptionsAdvanced.SelectOnSongClick.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsAdvanced.SelectOnSongClick, Ini.OnSongClick, IOnSongClickTranslated);
+
+ Theme.OptionsAdvanced.SelectAskbeforeDel.showArrows := true;
+ Theme.OptionsAdvanced.SelectAskbeforeDel.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsAdvanced.SelectAskbeforeDel, Ini.AskBeforeDel, IAskbeforeDelTranslated);
+
+ Theme.OptionsAdvanced.SelectPartyPopup.showArrows := true;
+ Theme.OptionsAdvanced.SelectPartyPopup.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsAdvanced.SelectPartyPopup, Ini.PartyPopup, IPartyPopupTranslated);
+
+ AddButton(Theme.OptionsAdvanced.ButtonExit);
+ if (Length(Button[0].Text)=0) then
+ AddButtonText(20, 5, Theme.Options.Description[7]);
+
+ Interaction := 0;
+end;
+
+procedure TScreenOptionsAdvanced.OnShow;
+begin
+ inherited;
+
+ Interaction := 0;
+end;
+
+end.
diff --git a/src/screens/UScreenOptionsGame.pas b/src/screens/UScreenOptionsGame.pas
new file mode 100644
index 00000000..39de61e4
--- /dev/null
+++ b/src/screens/UScreenOptionsGame.pas
@@ -0,0 +1,175 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOptionsGame;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UFiles,
+ UIni,
+ UThemes,
+ USongs;
+
+type
+ TScreenOptionsGame = class(TMenu)
+ public
+ old_Tabs, old_Sorting: integer;
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure RefreshSongs;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UUnicodeUtils,
+ SysUtils;
+
+function TScreenOptionsGame.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if PressedDown then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ RefreshSongs;
+ FadeTo(@ScreenOptions);
+ end;
+ SDLK_RETURN:
+ begin
+ if SelInteraction = 6 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ RefreshSongs;
+ FadeTo(@ScreenOptions);
+ end;
+ end;
+ SDLK_DOWN:
+ InteractNext;
+ SDLK_UP :
+ InteractPrev;
+ SDLK_RIGHT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction <= 5) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction <= 5) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+ end;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenOptionsGame.Create;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.OptionsGame);
+
+ //Refresh Songs Patch
+ old_Sorting := Ini.Sorting;
+ old_Tabs := Ini.Tabs;
+
+ Theme.OptionsGame.SelectPlayers.showArrows := true;
+ Theme.OptionsGame.SelectPlayers.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGame.SelectPlayers, Ini.Players, IPlayers);
+
+ Theme.OptionsGame.SelectDifficulty.showArrows := true;
+ Theme.OptionsGame.SelectDifficulty.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGame.SelectDifficulty, Ini.Difficulty, IDifficultyTranslated);
+
+ Theme.OptionsGame.SelectLanguage.showArrows := true;
+ Theme.OptionsGame.SelectLanguage.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGame.SelectLanguage, Ini.Language, ILanguageTranslated);
+
+ Theme.OptionsGame.SelectTabs.showArrows := true;
+ Theme.OptionsGame.SelectTabs.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGame.SelectTabs, Ini.Tabs, ITabsTranslated);
+
+ Theme.OptionsGame.SelectSorting.showArrows := true;
+ Theme.OptionsGame.SelectSorting.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGame.SelectSorting, Ini.Sorting, ISortingTranslated);
+
+ Theme.OptionsGame.SelectDebug.showArrows := true;
+ Theme.OptionsGame.SelectDebug.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGame.SelectDebug, Ini.Debug, IDebugTranslated);
+
+
+
+ AddButton(Theme.OptionsGame.ButtonExit);
+ if (Length(Button[0].Text) = 0) then
+ AddButtonText(20, 5, Theme.Options.Description[7]);
+
+end;
+
+//Refresh Songs Patch
+procedure TScreenOptionsGame.RefreshSongs;
+begin
+ if (ini.Sorting <> old_Sorting) or (ini.Tabs <> old_Tabs) then
+ ScreenSong.Refresh;
+end;
+
+procedure TScreenOptionsGame.OnShow;
+begin
+ inherited;
+
+// Interaction := 0;
+end;
+
+end.
diff --git a/src/screens/UScreenOptionsGraphics.pas b/src/screens/UScreenOptionsGraphics.pas
new file mode 100644
index 00000000..e2aacccd
--- /dev/null
+++ b/src/screens/UScreenOptionsGraphics.pas
@@ -0,0 +1,164 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOptionsGraphics;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UDisplay,
+ UMusic,
+ UFiles,
+ UIni,
+ UThemes;
+
+type
+ TScreenOptionsGraphics = class(TMenu)
+ public
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UUnicodeUtils,
+ SysUtils;
+
+function TScreenOptionsGraphics.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ SDLK_RETURN:
+ begin
+ if SelInteraction = 6 then
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ // FIXME: changing the video mode does not work this way in windows
+ // and MacOSX as all textures will be invalidated through this.
+ // See the ALT+TAB code too.
+ {$IF Defined(Linux) or Defined(FreeBSD)}
+ Reinitialize3D();
+ {$IFEND}
+ FadeTo(@ScreenOptions);
+ end;
+ end;
+ SDLK_DOWN:
+ InteractNext;
+ SDLK_UP :
+ InteractPrev;
+ SDLK_RIGHT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction < 6) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction < 6) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+ end;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenOptionsGraphics.Create;
+begin
+ inherited Create;
+ LoadFromTheme(Theme.OptionsGraphics);
+
+ Theme.OptionsGraphics.SelectResolution.showArrows := true;
+ Theme.OptionsGraphics.SelectResolution.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGraphics.SelectResolution, Ini.Resolution, IResolution);
+
+ Theme.OptionsGraphics.SelectFullscreen.showArrows := true;
+ Theme.OptionsGraphics.SelectFullscreen.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGraphics.SelectFullscreen, Ini.Fullscreen, IFullScreenTranslated);
+
+ Theme.OptionsGraphics.SelectDepth.showArrows := true;
+ Theme.OptionsGraphics.SelectDepth.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGraphics.SelectDepth, Ini.Depth, IDepth);
+
+ Theme.OptionsGraphics.SelectVisualizer.showArrows := true;
+ Theme.OptionsGraphics.SelectVisualizer.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGraphics.SelectVisualizer, Ini.VisualizerOption, IVisualizerTranslated);
+
+ Theme.OptionsGraphics.SelectOscilloscope.showArrows := true;
+ Theme.OptionsGraphics.SelectOscilloscope.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGraphics.SelectOscilloscope, Ini.Oscilloscope, IOscilloscopeTranslated);
+
+ Theme.OptionsGraphics.SelectMovieSize.showArrows := true;
+ Theme.OptionsGraphics.SelectMovieSize.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsGraphics.SelectMovieSize, Ini.MovieSize, IMovieSizeTranslated);
+
+ AddButton(Theme.OptionsGraphics.ButtonExit);
+ if (Length(Button[0].Text)=0) then
+ AddButtonText(20, 5, Theme.Options.Description[7]);
+
+end;
+
+procedure TScreenOptionsGraphics.OnShow;
+begin
+ inherited;
+
+ Interaction := 0;
+end;
+
+end.
diff --git a/src/screens/UScreenOptionsLyrics.pas b/src/screens/UScreenOptionsLyrics.pas
new file mode 100644
index 00000000..468082de
--- /dev/null
+++ b/src/screens/UScreenOptionsLyrics.pas
@@ -0,0 +1,147 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOptionsLyrics;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UDisplay,
+ UMusic,
+ UFiles,
+ UIni,
+ UThemes;
+
+type
+ TScreenOptionsLyrics = class(TMenu)
+ public
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UUnicodeUtils,
+ SysUtils;
+
+function TScreenOptionsLyrics.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ SDLK_RETURN:
+ begin
+ if SelInteraction = 3 then
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ end;
+ SDLK_DOWN:
+ InteractNext;
+ SDLK_UP :
+ InteractPrev;
+ SDLK_RIGHT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction <= 3) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction <= 3) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+ end;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenOptionsLyrics.Create;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.OptionsLyrics);
+
+ Theme.OptionsLyrics.SelectLyricsFont.showArrows := true;
+ Theme.OptionsLyrics.SelectLyricsFont.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsLyrics.SelectLyricsFont, Ini.LyricsFont, ILyricsFontTranslated);
+
+ Theme.OptionsLyrics.SelectLyricsEffect.showArrows := true;
+ Theme.OptionsLyrics.SelectLyricsEffect.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsLyrics.SelectLyricsEffect, Ini.LyricsEffect, ILyricsEffectTranslated);
+
+ Theme.OptionsLyrics.SelectNoteLines.showArrows := true;
+ Theme.OptionsLyrics.SelectNoteLines.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsLyrics.SelectNoteLines, Ini.NoteLines, INoteLinesTranslated);
+
+ AddButton(Theme.OptionsLyrics.ButtonExit);
+ if (Length(Button[0].Text)=0) then
+ AddButtonText(20, 5, Theme.Options.Description[7]);
+
+end;
+
+procedure TScreenOptionsLyrics.OnShow;
+begin
+ inherited;
+
+ Interaction := 0;
+end;
+
+end.
diff --git a/src/screens/UScreenOptionsRecord.pas b/src/screens/UScreenOptionsRecord.pas
new file mode 100644
index 00000000..7af598e5
--- /dev/null
+++ b/src/screens/UScreenOptionsRecord.pas
@@ -0,0 +1,836 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOptionsRecord;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UThemes,
+ UMusic,
+ URecord,
+ UMenu;
+
+type
+ TDrawState = record
+ ChannelIndex: integer;
+ R, G, B: real; // mapped player color (normal)
+ RD, GD, BD: real; // mapped player color (dark)
+ end;
+
+ TPeakInfo = record
+ Volume: single;
+ Time: cardinal;
+ end;
+
+ TScreenOptionsRecord = class(TMenu)
+ private
+ // max. count of input-channels determined for all devices
+ MaxChannelCount: integer;
+
+ // current input device
+ CurrentDeviceIndex: integer;
+ PreviewDeviceIndex: integer;
+
+ // string arrays for select-slide options
+ InputSourceNames: array of UTF8String;
+ InputDeviceNames: array of UTF8String;
+
+ // dynamic generated themes for channel select-sliders
+ SelectSlideChannelTheme: array of TThemeSelectSlide;
+
+ // indices for widget-updates
+ SelectInputSourceID: integer;
+ SelectSlideChannelID: array of integer;
+
+ // interaction IDs
+ ExitButtonIID: integer;
+
+ // dummy data for non-available channels
+ ChannelToPlayerMapDummy: integer;
+
+ // preview channel-buffers
+ PreviewChannel: array of TCaptureBuffer;
+ ChannelPeak: array of TPeakInfo;
+
+ // Device source volume
+ SourceVolume: single;
+ NextVolumePollTime: cardinal;
+
+ procedure StartPreview;
+ procedure StopPreview;
+ procedure UpdateInputDevice;
+ function ValidateSettings: boolean;
+ procedure ChangeVolume(VolumeChange: single);
+ procedure DrawVolume(x, y, Width, Height: single);
+ procedure DrawVUMeter(const State: TDrawState; x, y, Width, Height: single);
+ procedure DrawPitch(const State: TDrawState; x, y, Width, Height: single);
+ public
+ constructor Create; override;
+ function Draw: boolean; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure OnHide; override;
+ end;
+
+const
+ PeakDecay = 0.2; // strength of peak-decay (reduction after one sec)
+
+const
+ BarHeight = 11; // height of each bar (volume/vu-meter/pitch)
+ BarUpperSpacing = 1; // spacing between a bar-area and the previous widget
+ BarLowerSpacing = 3; // spacing between a bar-area and the next widget
+ SourceBarsTotalHeight = BarHeight + BarUpperSpacing + BarLowerSpacing;
+ ChannelBarsTotalHeight = 2*BarHeight + BarUpperSpacing + BarLowerSpacing;
+
+implementation
+
+uses
+ SysUtils,
+ Math,
+ SDL,
+ gl,
+ TextGL,
+ UGraphic,
+ UDraw,
+ ULanguage,
+ UMain,
+ UMenuSelectSlide,
+ UMenuText,
+ UFiles,
+ UDisplay,
+ UIni,
+ UUnicodeUtils,
+ ULog;
+
+function TScreenOptionsRecord.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ Ord('+'):
+ begin
+ // FIXME: add a nice volume-slider instead
+ // or at least provide visualization and acceleration if the user holds the key pressed.
+ ChangeVolume(0.02);
+ end;
+ Ord('-'):
+ begin
+ // FIXME: add a nice volume-slider instead
+ // or at least provide visualization and acceleration if the user holds the key pressed.
+ ChangeVolume(-0.02);
+ end;
+ Ord('T'):
+ begin
+ if ((SDL_GetModState() and KMOD_SHIFT) <> 0) then
+ Ini.ThresholdIndex := (Ini.ThresholdIndex + Length(IThresholdVals) - 1) mod Length(IThresholdVals)
+ else
+ Ini.ThresholdIndex := (Ini.ThresholdIndex + 1) mod Length(IThresholdVals);
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE:
+ begin
+ // TODO: Show Save/Abort screen
+ if (ValidateSettings()) then
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ end;
+ SDLK_RETURN:
+ begin
+ if (SelInteraction = ExitButtonIID) then
+ begin
+ if (ValidateSettings()) then
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ end;
+ end;
+ SDLK_DOWN:
+ InteractNext;
+ SDLK_UP :
+ InteractPrev;
+ SDLK_RIGHT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction < ExitButtonIID) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+ end;
+ UpdateInputDevice;
+ end;
+ SDLK_LEFT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction < ExitButtonIID) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+ end;
+ UpdateInputDevice;
+ end;
+ end;
+ end;
+end;
+
+function TScreenOptionsRecord.ValidateSettings: boolean;
+var
+ BadPlayer: integer;
+begin
+ BadPlayer := AudioInputProcessor.ValidateSettings();
+ if (BadPlayer <> 0) then
+ begin
+ ScreenPopupError.ShowPopup(
+ Format(Language.Translate('ERROR_PLAYER_DEVICE_ASSIGNMENT'),
+ [BadPlayer]));
+ Result := false;
+ end
+ else
+ begin
+ Result := true;
+ end;
+end;
+
+constructor TScreenOptionsRecord.Create;
+var
+ DeviceIndex: integer;
+ SourceIndex: integer;
+ ChannelIndex: integer;
+ InputDevice: TAudioInputDevice;
+ InputDeviceCfg: PInputDeviceConfig;
+ ChannelTheme: ^TThemeSelectSlide;
+ //ButtonTheme: TThemeButton;
+ WidgetYPos: integer;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.OptionsRecord);
+
+ // set CurrentDeviceIndex to a valid device
+ if (Length(AudioInputProcessor.DeviceList) > 0) then
+ CurrentDeviceIndex := 0
+ else
+ CurrentDeviceIndex := -1;
+
+ PreviewDeviceIndex := -1;
+
+ WidgetYPos := 0;
+
+ // init sliders if at least one device was detected
+ if (Length(AudioInputProcessor.DeviceList) > 0) then
+ begin
+ InputDevice := AudioInputProcessor.DeviceList[CurrentDeviceIndex];
+ InputDeviceCfg := @Ini.InputDeviceConfig[InputDevice.CfgIndex];
+
+ // init device-selection slider
+ SetLength(InputDeviceNames, Length(AudioInputProcessor.DeviceList));
+ for DeviceIndex := 0 to High(AudioInputProcessor.DeviceList) do
+ begin
+ InputDeviceNames[DeviceIndex] := AudioInputProcessor.DeviceList[DeviceIndex].Name;
+ end;
+ // add device-selection slider (InteractionID: 0)
+ Theme.OptionsRecord.SelectSlideCard.showArrows := true;
+ Theme.OptionsRecord.SelectSlideCard.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsRecord.SelectSlideCard, CurrentDeviceIndex, InputDeviceNames);
+
+ // init source-selection slider
+ SetLength(InputSourceNames, Length(InputDevice.Source));
+ for SourceIndex := 0 to High(InputDevice.Source) do
+ begin
+ InputSourceNames[SourceIndex] := InputDevice.Source[SourceIndex].Name;
+ end;
+
+ Theme.OptionsRecord.SelectSlideInput.showArrows := true;
+ Theme.OptionsRecord.SelectSlideInput.oneItemOnly := true;
+ // add source-selection slider (InteractionID: 1)
+ SelectInputSourceID := AddSelectSlide(Theme.OptionsRecord.SelectSlideInput,
+ InputDeviceCfg.Input, InputSourceNames);
+
+ // add space for source volume bar
+ WidgetYPos := Theme.OptionsRecord.SelectSlideInput.Y +
+ Theme.OptionsRecord.SelectSlideInput.H +
+ SourceBarsTotalHeight;
+
+ // find max. channel count of all devices
+ MaxChannelCount := 0;
+ for DeviceIndex := 0 to High(AudioInputProcessor.DeviceList) do
+ begin
+ if (AudioInputProcessor.DeviceList[DeviceIndex].AudioFormat.Channels > MaxChannelCount) then
+ MaxChannelCount := AudioInputProcessor.DeviceList[DeviceIndex].AudioFormat.Channels;
+ end;
+
+ // init channel-to-player mapping sliders
+ SetLength(SelectSlideChannelID, MaxChannelCount);
+ SetLength(SelectSlideChannelTheme, MaxChannelCount);
+
+ for ChannelIndex := 0 to MaxChannelCount-1 do
+ begin
+ // copy reference slide
+ SelectSlideChannelTheme[ChannelIndex] :=
+ Theme.OptionsRecord.SelectSlideChannel;
+ // set current channel-theme
+ ChannelTheme := @SelectSlideChannelTheme[ChannelIndex];
+ // adjust vertical position
+ ChannelTheme.Y := WidgetYPos;
+ // calc size of next slide (add space for bars)
+ WidgetYPos := WidgetYPos + ChannelTheme.H + ChannelBarsTotalHeight;
+ // append channel index to name
+ ChannelTheme.Text := ChannelTheme.Text + IntToStr(ChannelIndex+1);
+
+ // show/hide widgets depending on whether the channel exists
+ if (ChannelIndex < Length(InputDeviceCfg.ChannelToPlayerMap)) then
+ begin
+ // current device has this channel
+
+ // add slider
+ SelectSlideChannelID[ChannelIndex] := AddSelectSlide(ChannelTheme^,
+ InputDeviceCfg.ChannelToPlayerMap[ChannelIndex], IChannelPlayerTranslated);
+ end
+ else
+ begin
+ // current device does not have that many channels
+
+ // add slider but hide it and assign a dummy variable to it
+ SelectSlideChannelID[ChannelIndex] := AddSelectSlide(ChannelTheme^,
+ ChannelToPlayerMapDummy, IChannelPlayerTranslated);
+ SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := false;
+ end;
+ end;
+ end;
+
+ // add Exit-button
+ AddButton(Theme.OptionsRecord.ButtonExit);
+ if (Length(Button[0].Text) = 0) then
+ AddButtonText(20, 5, Theme.Options.Description[7]);
+ // store InteractionID
+ if (Length(AudioInputProcessor.DeviceList) > 0) then
+ ExitButtonIID := MaxChannelCount + 2
+ else
+ ExitButtonIID := 0;
+
+ // set focus
+ Interaction := 0;
+end;
+
+procedure TScreenOptionsRecord.UpdateInputDevice;
+var
+ SourceIndex: integer;
+ InputDevice: TAudioInputDevice;
+ InputDeviceCfg: PInputDeviceConfig;
+ ChannelIndex: integer;
+begin
+ //Log.LogStatus('Update input-device', 'TScreenOptionsRecord.UpdateCard') ;
+
+ StopPreview();
+
+ // set CurrentDeviceIndex to a valid device
+ if (CurrentDeviceIndex > High(AudioInputProcessor.DeviceList)) then
+ CurrentDeviceIndex := 0;
+
+ // update sliders if at least one device was detected
+ if (Length(AudioInputProcessor.DeviceList) > 0) then
+ begin
+ InputDevice := AudioInputProcessor.DeviceList[CurrentDeviceIndex];
+ InputDeviceCfg := @Ini.InputDeviceConfig[InputDevice.CfgIndex];
+
+ // update source-selection slider
+ SetLength(InputSourceNames, Length(InputDevice.Source));
+ for SourceIndex := 0 to High(InputDevice.Source) do
+ begin
+ InputSourceNames[SourceIndex] := InputDevice.Source[SourceIndex].Name;
+ end;
+ UpdateSelectSlideOptions(Theme.OptionsRecord.SelectSlideInput, SelectInputSourceID,
+ InputSourceNames, InputDeviceCfg.Input);
+
+ // update channel-to-player mapping sliders
+ for ChannelIndex := 0 to MaxChannelCount-1 do
+ begin
+ // show/hide widgets depending on whether the channel exists
+ if (ChannelIndex < Length(InputDeviceCfg.ChannelToPlayerMap)) then
+ begin
+ // current device has this channel
+
+ // show slider
+ UpdateSelectSlideOptions(SelectSlideChannelTheme[ChannelIndex],
+ SelectSlideChannelID[ChannelIndex], IChannelPlayerTranslated,
+ InputDeviceCfg.ChannelToPlayerMap[ChannelIndex]);
+ SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := true;
+ end
+ else
+ begin
+ // current device does not have that many channels
+
+ // hide slider and assign a dummy variable to it
+ UpdateSelectSlideOptions(SelectSlideChannelTheme[ChannelIndex],
+ SelectSlideChannelID[ChannelIndex], IChannelPlayerTranslated,
+ ChannelToPlayerMapDummy);
+ SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := false;
+ end;
+ end;
+ end;
+
+ StartPreview();
+end;
+
+procedure TScreenOptionsRecord.ChangeVolume(VolumeChange: single);
+var
+ InputDevice: TAudioInputDevice;
+ Volume: single;
+begin
+ // validate CurrentDeviceIndex
+ if ((CurrentDeviceIndex < 0) or
+ (CurrentDeviceIndex > High(AudioInputProcessor.DeviceList))) then
+ begin
+ Exit;
+ end;
+
+ InputDevice := AudioInputProcessor.DeviceList[CurrentDeviceIndex];
+ if not assigned(InputDevice) then
+ Exit;
+
+ // set new volume
+ Volume := InputDevice.GetVolume() + VolumeChange;
+ InputDevice.SetVolume(Volume);
+ //DebugWriteln('Volume: ' + floattostr(InputDevice.GetVolume));
+
+ // volume must be polled again
+ NextVolumePollTime := 0;
+end;
+
+procedure TScreenOptionsRecord.OnShow;
+var
+ ChannelIndex: integer;
+begin
+ inherited;
+
+ // BgMusic distracts too much, pause it
+ SoundLib.PauseBgMusic;
+
+ Interaction := 0;
+
+ // create preview sound-buffers
+ SetLength(PreviewChannel, MaxChannelCount);
+ for ChannelIndex := 0 to High(PreviewChannel) do
+ PreviewChannel[ChannelIndex] := TCaptureBuffer.Create();
+
+ SetLength(ChannelPeak, MaxChannelCount);
+
+ UpdateInputDevice();
+end;
+
+procedure TScreenOptionsRecord.OnHide;
+var
+ ChannelIndex: integer;
+begin
+ StopPreview();
+
+ // free preview buffers
+ for ChannelIndex := 0 to High(PreviewChannel) do
+ PreviewChannel[ChannelIndex].Free;
+ SetLength(PreviewChannel, 0);
+ SetLength(ChannelPeak, 0);
+end;
+
+procedure TScreenOptionsRecord.StartPreview;
+var
+ ChannelIndex: integer;
+ Device: TAudioInputDevice;
+begin
+ if ((CurrentDeviceIndex >= 0) and
+ (CurrentDeviceIndex <= High(AudioInputProcessor.DeviceList))) then
+ begin
+ Device := AudioInputProcessor.DeviceList[CurrentDeviceIndex];
+ // set preview channel as active capture channel
+ for ChannelIndex := 0 to High(Device.CaptureChannel) do
+ begin
+ PreviewChannel[ChannelIndex].Clear();
+ Device.LinkCaptureBuffer(ChannelIndex, PreviewChannel[ChannelIndex]);
+ FillChar(ChannelPeak[ChannelIndex], SizeOf(TPeakInfo), 0);
+ end;
+ Device.Start();
+ PreviewDeviceIndex := CurrentDeviceIndex;
+
+ // volume must be polled again
+ NextVolumePollTime := 0;
+ end;
+end;
+
+procedure TScreenOptionsRecord.StopPreview;
+var
+ ChannelIndex: integer;
+ Device: TAudioInputDevice;
+begin
+ if ((PreviewDeviceIndex >= 0) and
+ (PreviewDeviceIndex <= High(AudioInputProcessor.DeviceList))) then
+ begin
+ Device := AudioInputProcessor.DeviceList[PreviewDeviceIndex];
+ Device.Stop;
+ for ChannelIndex := 0 to High(Device.CaptureChannel) do
+ Device.LinkCaptureBuffer(ChannelIndex, nil);
+ end;
+ PreviewDeviceIndex := -1;
+end;
+
+procedure TScreenOptionsRecord.DrawVolume(x, y, Width, Height: single);
+var
+ x1, y1, x2, y2: single;
+ VolBarInnerWidth: integer;
+ Volume: single;
+const
+ VolBarInnerHSpacing = 2;
+ VolBarInnerVSpacing = 1;
+begin
+ // coordinates for black rect
+ x1 := x;
+ y1 := y;
+ x2 := x1 + Width;
+ y2 := y1 + Height;
+
+ // init blend mode
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ // draw black background-rect
+ glColor4f(0, 0, 0, 0.8);
+ glBegin(GL_QUADS);
+ glVertex2f(x1, y1);
+ glVertex2f(x2, y1);
+ glVertex2f(x2, y2);
+ glVertex2f(x1, y2);
+ glEnd();
+
+ VolBarInnerWidth := Trunc(Width - 2*VolBarInnerHSpacing);
+
+ // TODO: if no volume is available, show some info (a blue bar maybe)
+ if (SourceVolume >= 0) then
+ Volume := SourceVolume
+ else
+ Volume := 0;
+
+ // coordinates for first half of the volume bar
+ x1 := x + VolBarInnerHSpacing;
+ x2 := x1 + VolBarInnerWidth * Volume;
+ y1 := y1 + VolBarInnerVSpacing;
+ y2 := y2 - VolBarInnerVSpacing;
+
+ // draw volume-bar
+ glBegin(GL_QUADS);
+ // draw volume bar
+ glColor3f(0.4, 0.3, 0.3);
+ glVertex2f(x1, y1);
+ glVertex2f(x1, y2);
+ glColor3f(1, 0.1, 0.1);
+ glVertex2f(x2, y2);
+ glVertex2f(x2, y1);
+ glEnd();
+
+ { not needed anymore
+ // coordinates for separator
+ x1 := x + VolBarInnerHSpacing;
+ x2 := x1 + VolBarInnerWidth;
+
+ // draw separator
+ glBegin(GL_LINE_STRIP);
+ glColor4f(0.1, 0.1, 0.1, 0.2);
+ glVertex2f(x1, y2);
+ glColor4f(0.4, 0.4, 0.4, 0.2);
+ glVertex2f((x1+x2)/2, y2);
+ glColor4f(0.1, 0.1, 0.1, 0.2);
+ glVertex2f(x2, y2);
+ glEnd();
+ }
+
+ glDisable(GL_BLEND);
+end;
+
+procedure TScreenOptionsRecord.DrawVUMeter(const State: TDrawState; x, y, Width, Height: single);
+var
+ x1, y1, x2, y2: single;
+ Volume, PeakVolume: single;
+ Delta: single;
+ VolBarInnerWidth: integer;
+const
+ VolBarInnerHSpacing = 2;
+ VolBarInnerVSpacing = 1;
+begin
+ // coordinates for black rect
+ x1 := x;
+ y1 := y;
+ x2 := x1 + Width;
+ y2 := y1 + Height;
+
+ // init blend mode
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ // draw black background-rect
+ glColor4f(0, 0, 0, 0.8);
+ glBegin(GL_QUADS);
+ glVertex2f(x1, y1);
+ glVertex2f(x2, y1);
+ glVertex2f(x2, y2);
+ glVertex2f(x1, y2);
+ glEnd();
+
+ VolBarInnerWidth := Trunc(Width - 2*VolBarInnerHSpacing);
+
+ // vertical positions
+ y1 := y1 + VolBarInnerVSpacing;
+ y2 := y2 - VolBarInnerVSpacing;
+
+ // coordinates for bevel
+ x1 := x + VolBarInnerHSpacing;
+ x2 := x1 + VolBarInnerWidth;
+
+ glBegin(GL_QUADS);
+ Volume := PreviewChannel[State.ChannelIndex].MaxSampleVolume();
+
+ // coordinates for volume bar
+ x1 := x + VolBarInnerHSpacing;
+ x2 := x1 + VolBarInnerWidth * Volume;
+
+ // draw volume bar
+ glColor3f(State.RD, State.GD, State.BD);
+ glVertex2f(x1, y1);
+ glVertex2f(x1, y2);
+ glColor3f(State.R, State.G, State.B);
+ glVertex2f(x2, y2);
+ glVertex2f(x2, y1);
+
+ Delta := (SDL_GetTicks() - ChannelPeak[State.ChannelIndex].Time)/1000;
+ PeakVolume := ChannelPeak[State.ChannelIndex].Volume - Delta*Delta*PeakDecay;
+
+ // determine new peak-volume
+ if (Volume > PeakVolume) then
+ begin
+ PeakVolume := Volume;
+ ChannelPeak[State.ChannelIndex].Volume := Volume;
+ ChannelPeak[State.ChannelIndex].Time := SDL_GetTicks();
+ end;
+
+ x1 := x + VolBarInnerHSpacing + VolBarInnerWidth * PeakVolume;
+ x2 := x1 + 2;
+
+ // draw peak
+ glColor3f(0.8, 0.8, 0.8);
+ glVertex2f(x1, y1);
+ glVertex2f(x1, y2);
+ glVertex2f(x2, y2);
+ glVertex2f(x2, y1);
+
+ // draw threshold
+ x1 := x + VolBarInnerHSpacing;
+ x2 := x1 + VolBarInnerWidth * IThresholdVals[Ini.ThresholdIndex];
+
+ glColor4f(0.3, 0.3, 0.3, 0.6);
+ glVertex2f(x1, y1);
+ glVertex2f(x1, y2);
+ glVertex2f(x2, y2);
+ glVertex2f(x2, y1);
+ glEnd();
+
+ glDisable(GL_BLEND);
+end;
+
+procedure TScreenOptionsRecord.DrawPitch(const State: TDrawState; x, y, Width, Height: single);
+var
+ x1, y1, x2, y2: single;
+ i: integer;
+ ToneBoxWidth: real;
+ ToneString: string;
+ ToneStringWidth, ToneStringHeight: real;
+ ToneStringMaxWidth: real;
+ ToneStringCenterXOffset: real;
+const
+ PitchBarInnerHSpacing = 2;
+ PitchBarInnerVSpacing = 1;
+begin
+ // calc tone pitch
+ PreviewChannel[State.ChannelIndex].AnalyzeBuffer();
+
+ // coordinates for black rect
+ x1 := x;
+ y1 := y;
+ x2 := x + Width;
+ y2 := y + Height;
+
+ // init blend mode
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ // draw black background-rect
+ glColor4f(0, 0, 0, 0.8);
+ glBegin(GL_QUADS);
+ glVertex2f(x1, y1);
+ glVertex2f(x2, y1);
+ glVertex2f(x2, y2);
+ glVertex2f(x1, y2);
+ glEnd();
+
+ // coordinates for tone boxes
+ ToneBoxWidth := Width / NumHalftones;
+ y1 := y1 + PitchBarInnerVSpacing;
+ y2 := y2 - PitchBarInnerVSpacing;
+
+ glBegin(GL_QUADS);
+ // draw tone boxes
+ for i := 0 to NumHalftones-1 do
+ begin
+ x1 := x + i * ToneBoxWidth + PitchBarInnerHSpacing;
+ x2 := x1 + ToneBoxWidth - 2*PitchBarInnerHSpacing;
+
+ if ((PreviewChannel[State.ChannelIndex].ToneValid) and
+ (PreviewChannel[State.ChannelIndex].ToneAbs = i)) then
+ begin
+ // highlight current tone-pitch
+ glColor3f(1, i / (NumHalftones-1), 0)
+ end
+ else
+ begin
+ // grey other tone-pitches
+ glColor3f(0.3, i / (NumHalftones-1) * 0.3, 0);
+ end;
+
+ glVertex2f(x1, y1);
+ glVertex2f(x2, y1);
+ glVertex2f(x2, y2);
+ glVertex2f(x1, y2);
+ end;
+ glEnd();
+
+ glDisable(GL_BLEND);
+
+ ///
+ // draw the name of the tone
+ ///////
+
+ ToneString := PreviewChannel[State.ChannelIndex].ToneString;
+ ToneStringHeight := ChannelBarsTotalHeight;
+
+ // initialize font
+ // TODO: what about reflection, italic etc.?
+ SetFontSize(ToneStringHeight);
+
+ // center
+ // Note: for centering let us assume that G#4 has the max. horizontal extent
+ ToneStringWidth := glTextWidth(ToneString);
+ ToneStringMaxWidth := glTextWidth('G#4');
+ ToneStringCenterXOffset := (ToneStringMaxWidth-ToneStringWidth) / 2;
+
+ // draw
+ SetFontPos(x-ToneStringWidth-ToneStringCenterXOffset, y-ToneStringHeight/2);
+ glColor3f(0, 0, 0);
+ glPrint(ToneString);
+end;
+
+function TScreenOptionsRecord.Draw: boolean;
+var
+ Device: TAudioInputDevice;
+ DeviceCfg: PInputDeviceConfig;
+ SelectSlide: TSelectSlide;
+ BarXOffset, BarYOffset, BarWidth: real;
+ ChannelIndex: integer;
+ State: TDrawState;
+begin
+ DrawBG;
+ DrawFG;
+
+ if ((PreviewDeviceIndex >= 0) and
+ (PreviewDeviceIndex <= High(AudioInputProcessor.DeviceList))) then
+ begin
+ Device := AudioInputProcessor.DeviceList[PreviewDeviceIndex];
+ DeviceCfg := @Ini.InputDeviceConfig[Device.CfgIndex];
+
+ // update source volume
+ if (SDL_GetTicks() >= NextVolumePollTime) then
+ begin
+ NextVolumePollTime := SDL_GetTicks() + 500; // next poll in 500ms
+ SourceVolume := Device.GetVolume();
+ end;
+
+ // get source select slide
+ SelectSlide := SelectsS[SelectInputSourceID];
+ BarXOffset := SelectSlide.TextureSBG.X;
+ BarYOffset := SelectSlide.TextureSBG.Y + SelectSlide.TextureSBG.H + BarUpperSpacing;
+ BarWidth := SelectSlide.TextureSBG.W;
+ DrawVolume(SelectSlide.TextureSBG.X, BarYOffset, BarWidth, BarHeight);
+
+ for ChannelIndex := 0 to High(Device.CaptureChannel) do
+ begin
+ // load player color mapped to current input channel
+ if (DeviceCfg.ChannelToPlayerMap[ChannelIndex] <> CHANNEL_OFF) then
+ begin
+ // set mapped channel to corresponding player-color
+ LoadColor(State.R, State.G, State.B, 'P'+ IntToStr(DeviceCfg.ChannelToPlayerMap[ChannelIndex]) + 'Dark');
+ end
+ else
+ begin
+ // set non-mapped channel to white
+ State.R := 1; State.G := 1; State.B := 1;
+ end;
+
+ // dark player colors
+ State.RD := 0.2 * State.R;
+ State.GD := 0.2 * State.G;
+ State.BD := 0.2 * State.B;
+
+ // channel select slide
+ SelectSlide := SelectsS[SelectSlideChannelID[ChannelIndex]];
+
+ BarXOffset := SelectSlide.TextureSBG.X;
+ BarYOffset := SelectSlide.TextureSBG.Y + SelectSlide.TextureSBG.H + BarUpperSpacing;
+ BarWidth := SelectSlide.TextureSBG.W;
+
+ State.ChannelIndex := ChannelIndex;
+
+ DrawVUMeter(State, BarXOffset, BarYOffset, BarWidth, BarHeight);
+ DrawPitch(State, BarXOffset, BarYOffset+BarHeight, BarWidth, BarHeight);
+ end;
+ end;
+
+ Result := true;
+end;
+
+end.
diff --git a/src/screens/UScreenOptionsSound.pas b/src/screens/UScreenOptionsSound.pas
new file mode 100644
index 00000000..c0efa4d8
--- /dev/null
+++ b/src/screens/UScreenOptionsSound.pas
@@ -0,0 +1,187 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOptionsSound;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UFiles,
+ UIni,
+ UThemes;
+
+type
+ TScreenOptionsSound = class(TMenu)
+ public
+ constructor Create; override;
+ function ParseInput(PressedKey: Cardinal; CharCode: UCS4Char;
+ PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UUnicodeUtils,
+ SysUtils;
+
+function TScreenOptionsSound.ParseInput(PressedKey: cardinal;
+ CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE:
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ SDLK_RETURN:
+ begin
+ if SelInteraction = 8 then
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenOptions);
+ end;
+ end;
+ SDLK_DOWN:
+ InteractNext;
+ SDLK_UP:
+ InteractPrev;
+ SDLK_RIGHT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction < 8) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction < 8) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+ end;
+ end;
+ end;
+ end;
+
+{**
+ * Actually this one isn't pretty - but it does the trick of
+ * turning the background music on/off in "real time"
+ * bgm = background music
+ * TODO: - Fetching the SelectInteraction via something more descriptive
+ * - Obtaining the current value of a select is imho ugly
+ *}
+ if (SelInteraction = 1) then
+ begin
+ if TBackgroundMusicOption(SelectsS[1].SelectedOption) = bmoOn then
+ SoundLib.StartBgMusic
+ else
+ SoundLib.PauseBgMusic;
+ end;
+
+end;
+
+constructor TScreenOptionsSound.Create;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.OptionsSound);
+
+ Theme.OptionsSound.SelectSlideVoicePassthrough.showArrows := true;
+ Theme.OptionsSound.SelectSlideVoicePassthrough.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsSound.SelectSlideVoicePassthrough, Ini.VoicePassthrough, IVoicePassthroughTranslated);
+
+ Theme.OptionsSound.SelectBackgroundMusic.showArrows := true;
+ Theme.OptionsSound.SelectBackgroundMusic.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsSound.SelectBackgroundMusic, Ini.BackgroundMusicOption, IBackgroundMusicTranslated);
+
+ // TODO: - MicBoost needs to be moved to ScreenOptionsRecord
+ Theme.OptionsSound.SelectMicBoost.showArrows := true;
+ Theme.OptionsSound.SelectMicBoost.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsSound.SelectMicBoost, Ini.MicBoost, IMicBoostTranslated);
+
+
+ Theme.OptionsSound.SelectClickAssist.showArrows := true;
+ Theme.OptionsSound.SelectClickAssist.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsSound.SelectClickAssist, Ini.ClickAssist, IClickAssistTranslated);
+
+ Theme.OptionsSound.SelectBeatClick.showArrows := true;
+ Theme.OptionsSound.SelectBeatClick.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsSound.SelectBeatClick, Ini.BeatClick, IBeatClickTranslated);
+
+ Theme.OptionsSound.SelectThreshold.showArrows := true;
+ Theme.OptionsSound.SelectThreshold.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsSound.SelectThreshold, Ini.ThresholdIndex, IThreshold);
+
+ Theme.OptionsSound.SelectSlidePreviewVolume.showArrows := true;
+ Theme.OptionsSound.SelectSlidePreviewVolume.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsSound.SelectSlidePreviewVolume, Ini.PreviewVolume, IPreviewVolumeTranslated);
+
+ Theme.OptionsSound.SelectSlidePreviewFading.showArrows := true;
+ Theme.OptionsSound.SelectSlidePreviewFading.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsSound.SelectSlidePreviewFading, Ini.PreviewFading, IPreviewFadingTranslated);
+
+ AddButton(Theme.OptionsSound.ButtonExit);
+ if (Length(Button[0].Text) = 0) then
+ AddButtonText(20, 5, Theme.Options.Description[7]);
+
+ Interaction := 0;
+end;
+
+procedure TScreenOptionsSound.OnShow;
+begin
+ inherited;
+ Interaction := 0;
+end;
+
+end.
diff --git a/src/screens/UScreenOptionsThemes.pas b/src/screens/UScreenOptionsThemes.pas
new file mode 100644
index 00000000..29d8a9dc
--- /dev/null
+++ b/src/screens/UScreenOptionsThemes.pas
@@ -0,0 +1,237 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenOptionsThemes;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UFiles,
+ UIni,
+ UThemes;
+
+type
+ TScreenOptionsThemes = class(TMenu)
+ private
+ procedure ReloadTheme;
+ public
+ SkinSelect: integer;
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure InteractInc; override;
+ procedure InteractDec; override;
+ end;
+
+implementation
+
+uses
+ SysUtils,
+ UGraphic,
+ UMain,
+ UPathUtils,
+ UUnicodeUtils,
+ USkins;
+
+function TScreenOptionsThemes.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ Ini.Save;
+
+ // Reload all screens, after Theme changed
+ // Todo : JB - Check if theme was actually changed
+ UGraphic.UnLoadScreens();
+ UGraphic.LoadScreens();
+
+ AudioPlayback.PlaySound(SoundLib.Back);
+
+ // select theme button in new created options screen
+ ScreenOptions.Interaction := 4;
+
+ FadeTo(@ScreenOptions);
+ end;
+ SDLK_RETURN:
+ begin
+ if SelInteraction = 3 then
+ begin
+ Ini.Save;
+
+ // Reload all screens, after Theme changed
+ // Todo : JB - Check if theme was actually changed
+ UGraphic.UnLoadScreens();
+ UGraphic.LoadScreens();
+
+ AudioPlayback.PlaySound(SoundLib.Back);
+
+ // select theme button in new created options screen
+ ScreenOptions.Interaction := 4;
+
+ FadeTo(@ScreenOptions);
+ end;
+ end;
+ SDLK_DOWN:
+ InteractNext;
+ SDLK_UP :
+ InteractPrev;
+ SDLK_RIGHT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction <= 2) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ if (SelInteraction >= 0) and (SelInteraction <= 2) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure TScreenOptionsThemes.InteractInc;
+begin
+ inherited InteractInc;
+
+ //Update Skins
+ if (SelInteraction = 0) then
+ begin
+ Skin.OnThemeChange;
+ UpdateSelectSlideOptions(Theme.OptionsThemes.SelectSkin, SkinSelect, ISkin, Ini.SkinNo);
+
+ // set skin to themes default skin
+ Ini.SkinNo := Theme.Themes[Ini.Theme].DefaultSkin;
+ end;
+
+ { set skins default color }
+ if (SelInteraction = 0) or (SelInteraction = 1) then
+ begin
+ Ini.Color := Skin.GetDefaultColor(Ini.SkinNo);
+ end;
+
+ ReloadTheme();
+end;
+
+procedure TScreenOptionsThemes.InteractDec;
+begin
+ inherited InteractDec;
+
+ //Update Skins
+ if (SelInteraction = 0 ) then
+ begin
+ Skin.OnThemeChange;
+ UpdateSelectSlideOptions (Theme.OptionsThemes.SelectSkin, SkinSelect, ISkin, Ini.SkinNo);
+
+ // set skin to themes default skin
+ Ini.SkinNo := Theme.Themes[Ini.Theme].DefaultSkin;
+ end;
+
+ { set skins default color }
+ if (SelInteraction = 0) or (SelInteraction = 1) then
+ begin
+ Ini.Color := Skin.GetDefaultColor(Ini.SkinNo);
+ end;
+
+ ReloadTheme();
+end;
+
+constructor TScreenOptionsThemes.Create;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.OptionsThemes);
+
+ Theme.OptionsThemes.SelectTheme.showArrows := true;
+ Theme.OptionsThemes.SelectTheme.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsThemes.SelectTheme, Ini.Theme, ITheme);
+
+ Theme.OptionsThemes.SelectSkin.showArrows := true;
+ Theme.OptionsThemes.SelectSkin.oneItemOnly := true;
+ SkinSelect := AddSelectSlide(Theme.OptionsThemes.SelectSkin, Ini.SkinNo, ISkin);
+
+ Theme.OptionsThemes.SelectColor.showArrows := true;
+ Theme.OptionsThemes.SelectColor.oneItemOnly := true;
+ AddSelectSlide(Theme.OptionsThemes.SelectColor, Ini.Color, IColorTranslated);
+
+ AddButton(Theme.OptionsThemes.ButtonExit);
+ if (Length(Button[0].Text)=0) then
+ AddButtonText(20, 5, Theme.Options.Description[7]);
+end;
+
+procedure TScreenOptionsThemes.OnShow;
+begin
+ inherited;
+
+ Interaction := 0;
+end;
+
+procedure TScreenOptionsThemes.ReloadTheme;
+begin
+ Theme.LoadTheme(Ini.Theme, Ini.Color);
+
+ ScreenOptionsThemes := TScreenOptionsThemes.create();
+ ScreenOptionsThemes.onshow;
+ Display.CurrentScreen := @ScreenOptionsThemes;
+
+ ScreenOptionsThemes.Interaction := self.Interaction;
+ ScreenOptionsThemes.Draw;
+
+ Display.Draw;
+ SwapBuffers;
+
+ Self.Destroy;
+end;
+
+end.
diff --git a/src/screens/UScreenPartyNewRound.pas b/src/screens/UScreenPartyNewRound.pas
new file mode 100644
index 00000000..8024108c
--- /dev/null
+++ b/src/screens/UScreenPartyNewRound.pas
@@ -0,0 +1,334 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenPartyNewRound;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ SysUtils,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UFiles,
+ UThemes;
+
+type
+ TScreenPartyNewRound = class(TMenu)
+ public
+ //Texts:
+ TextRound: array [0..6] of cardinal;
+
+ TextWinner: array [0..6] of cardinal;
+
+ TextNextRound: cardinal;
+ TextNextRoundNo: cardinal;
+ TextNextPlayer1: cardinal;
+ TextNextPlayer2: cardinal;
+ TextNextPlayer3: cardinal;
+
+ //Statics
+ StaticRound: array [0..6] of cardinal;
+
+ //Scores
+ TextScoreTeam1: cardinal;
+ TextScoreTeam2: cardinal;
+ TextScoreTeam3: cardinal;
+ TextNameTeam1: cardinal;
+ TextNameTeam2: cardinal;
+ TextNameTeam3: cardinal;
+
+ TextTeam1Players: cardinal;
+ TextTeam2Players: cardinal;
+ TextTeam3Players: cardinal;
+
+ StaticTeam1: cardinal;
+ StaticTeam2: cardinal;
+ StaticTeam3: cardinal;
+ StaticNextPlayer1: cardinal;
+ StaticNextPlayer2: cardinal;
+ StaticNextPlayer3: cardinal;
+
+
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UIni,
+ UTexture,
+ UParty,
+ ULanguage,
+ USong,
+ ULog,
+ UUnicodeUtils;
+
+function TScreenPartyNewRound.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ CheckFadeTo(@ScreenMain,'MSG_END_PARTY');
+ end;
+
+ SDLK_RETURN:
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ Party.CallBeforeSongSelect;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenPartyNewRound.Create;
+begin
+ inherited Create;
+
+ TextRound[0] := AddText (Theme.PartyNewRound.TextRound1);
+ TextRound[1] := AddText (Theme.PartyNewRound.TextRound2);
+ TextRound[2] := AddText (Theme.PartyNewRound.TextRound3);
+ TextRound[3] := AddText (Theme.PartyNewRound.TextRound4);
+ TextRound[4] := AddText (Theme.PartyNewRound.TextRound5);
+ TextRound[5] := AddText (Theme.PartyNewRound.TextRound6);
+ TextRound[6] := AddText (Theme.PartyNewRound.TextRound7);
+
+ TextWinner[0] := AddText (Theme.PartyNewRound.TextWinner1);
+ TextWinner[1] := AddText (Theme.PartyNewRound.TextWinner2);
+ TextWinner[2] := AddText (Theme.PartyNewRound.TextWinner3);
+ TextWinner[3] := AddText (Theme.PartyNewRound.TextWinner4);
+ TextWinner[4] := AddText (Theme.PartyNewRound.TextWinner5);
+ TextWinner[5] := AddText (Theme.PartyNewRound.TextWinner6);
+ TextWinner[6] := AddText (Theme.PartyNewRound.TextWinner7);
+
+ TextNextRound := AddText (Theme.PartyNewRound.TextNextRound);
+ TextNextRoundNo := AddText (Theme.PartyNewRound.TextNextRoundNo);
+ TextNextPlayer1 := AddText (Theme.PartyNewRound.TextNextPlayer1);
+ TextNextPlayer2 := AddText (Theme.PartyNewRound.TextNextPlayer2);
+ TextNextPlayer3 := AddText (Theme.PartyNewRound.TextNextPlayer3);
+
+ StaticRound[0] := AddStatic (Theme.PartyNewRound.StaticRound1);
+ StaticRound[1] := AddStatic (Theme.PartyNewRound.StaticRound2);
+ StaticRound[2] := AddStatic (Theme.PartyNewRound.StaticRound3);
+ StaticRound[3] := AddStatic (Theme.PartyNewRound.StaticRound4);
+ StaticRound[4] := AddStatic (Theme.PartyNewRound.StaticRound5);
+ StaticRound[5] := AddStatic (Theme.PartyNewRound.StaticRound6);
+ StaticRound[6] := AddStatic (Theme.PartyNewRound.StaticRound7);
+
+ //Scores
+ TextScoreTeam1 := AddText (Theme.PartyNewRound.TextScoreTeam1);
+ TextScoreTeam2 := AddText (Theme.PartyNewRound.TextScoreTeam2);
+ TextScoreTeam3 := AddText (Theme.PartyNewRound.TextScoreTeam3);
+ TextNameTeam1 := AddText (Theme.PartyNewRound.TextNameTeam1);
+ TextNameTeam2 := AddText (Theme.PartyNewRound.TextNameTeam2);
+ TextNameTeam3 := AddText (Theme.PartyNewRound.TextNameTeam3);
+
+ //Players
+ TextTeam1Players := AddText (Theme.PartyNewRound.TextTeam1Players);
+ TextTeam2Players := AddText (Theme.PartyNewRound.TextTeam2Players);
+ TextTeam3Players := AddText (Theme.PartyNewRound.TextTeam3Players);
+
+ StaticTeam1 := AddStatic (Theme.PartyNewRound.StaticTeam1);
+ StaticTeam2 := AddStatic (Theme.PartyNewRound.StaticTeam2);
+ StaticTeam3 := AddStatic (Theme.PartyNewRound.StaticTeam3);
+ StaticNextPlayer1 := AddStatic (Theme.PartyNewRound.StaticNextPlayer1);
+ StaticNextPlayer2 := AddStatic (Theme.PartyNewRound.StaticNextPlayer2);
+ StaticNextPlayer3 := AddStatic (Theme.PartyNewRound.StaticNextPlayer3);
+
+ LoadFromTheme(Theme.PartyNewRound);
+end;
+
+procedure TScreenPartyNewRound.OnShow;
+var
+ I: integer;
+ function GetTeamPlayers(const Num: integer): UTF8String;
+ var
+ Players: array of UTF8String;
+ J: integer;
+ begin
+ if (Num > High(Party.Teams)) or (Num < 0) then
+ exit;
+
+ //Create Players array
+ SetLength(Players, Length(Party.Teams[Num].Players));
+ For J := 0 to High(Party.Teams[Num].Players) do
+ Players[J] := UTF8String(Party.Teams[Num].Players[J].Name);
+
+ //Implode and Return
+ Result := Language.Implode(Players);
+ end;
+begin
+ inherited;
+
+ //Set Visibility of Round Infos
+ for I := 0 to 6 do
+ begin
+ if (I <= High(Party.Rounds)) then
+ begin
+ Statics[StaticRound[I]].Visible := True;
+ Text[TextRound[I]].Visible := True;
+ Text[TextWinner[I]].Visible := True;
+
+ // update texts:
+ Text[TextRound[I]].Text := Language.Translate('MODE_' + uppercase(Party.Modes[Party.Rounds[I].Mode].Name) + '_NAME');
+ Text[TextWinner[I]].Text := Party.GetWinnerString(I);
+ end
+ else
+ begin
+ Statics[StaticRound[I]].Visible := False;
+ Text[TextRound[I]].Visible := False;
+ Text[TextWinner[I]].Visible := False;
+ end;
+ end;
+
+
+ //Display Scores
+ if (Length(Party.Teams) >= 1) then
+ begin
+ Text[TextScoreTeam1].Text := InttoStr(Party.Teams[0].Score);
+ Text[TextNameTeam1].Text := UTF8String(Party.Teams[0].Name);
+ Text[TextTeam1Players].Text := GetTeamPlayers(0);
+
+ Text[TextScoreTeam1].Visible := true;
+ Text[TextNameTeam1].Visible := true;
+ Text[TextTeam1Players].Visible := true;
+ Statics[StaticTeam1].Visible := true;
+ Statics[StaticNextPlayer1].Visible := true;
+ end
+ else
+ begin
+ Text[TextScoreTeam1].Visible := false;
+ Text[TextNameTeam1].Visible := false;
+ Text[TextTeam1Players].Visible := false;
+ Statics[StaticTeam1].Visible := false;
+ Statics[StaticNextPlayer1].Visible := false;
+ end;
+
+ if (Length(Party.Teams) >= 2) then
+ begin
+ Text[TextScoreTeam2].Text := InttoStr(Party.Teams[1].Score);
+ Text[TextNameTeam2].Text := UTF8String(Party.Teams[1].Name);
+ Text[TextTeam2Players].Text := GetTeamPlayers(1);
+
+ Text[TextScoreTeam2].Visible := true;
+ Text[TextNameTeam2].Visible := true;
+ Text[TextTeam2Players].Visible := true;
+ Statics[StaticTeam2].Visible := true;
+ Statics[StaticNextPlayer2].Visible := true;
+ end
+ else
+ begin
+ Text[TextScoreTeam2].Visible := false;
+ Text[TextNameTeam2].Visible := false;
+ Text[TextTeam2Players].Visible := false;
+ Statics[StaticTeam2].Visible := false;
+ Statics[StaticNextPlayer2].Visible := false;
+ end;
+
+ if (Length(Party.Teams) >= 3) then
+ begin
+ Text[TextScoreTeam3].Text := InttoStr(Party.Teams[2].Score);
+ Text[TextNameTeam3].Text := UTF8String(Party.Teams[2].Name);
+ Text[TextTeam3Players].Text := GetTeamPlayers(2);
+
+ Text[TextScoreTeam3].Visible := true;
+ Text[TextNameTeam3].Visible := true;
+ Text[TextTeam3Players].Visible := true;
+ Statics[StaticTeam3].Visible := true;
+ Statics[StaticNextPlayer3].Visible := true;
+ end
+ else
+ begin
+ Text[TextScoreTeam3].Visible := false;
+ Text[TextNameTeam3].Visible := false;
+ Text[TextTeam3Players].Visible := false;
+ Statics[StaticTeam3].Visible := false;
+ Statics[StaticNextPlayer3].Visible := false;
+ end;
+
+ //nextRound Texts
+ Text[TextNextRound].Text := Language.Translate('MODE_' + uppercase(Party.Modes[Party.Rounds[Party.CurrentRound].Mode].Name) + '_DESC');
+ Text[TextNextRoundNo].Text := InttoStr(Party.CurrentRound + 1);
+ if (Length(Party.Teams) >= 1) then
+ begin
+ Text[TextNextPlayer1].Text := Party.Teams[0].Players[Party.Teams[0].NextPlayer].Name;
+ Text[TextNextPlayer1].Visible := true;
+ end
+ else
+ Text[TextNextPlayer1].Visible := false;
+
+ if (Length(Party.Teams) >= 2) then
+ begin
+ Text[TextNextPlayer2].Text := Party.Teams[1].Players[Party.Teams[1].NextPlayer].Name;
+ Text[TextNextPlayer2].Visible := true;
+ end
+ else
+ Text[TextNextPlayer2].Visible := false;
+
+ if (Length(Party.Teams) >= 3) then
+ begin
+ Text[TextNextPlayer3].Text := Party.Teams[2].Players[Party.Teams[2].NextPlayer].Name;
+ Text[TextNextPlayer3].Visible := true;
+ end
+ else
+ Text[TextNextPlayer3].Visible := false;
+end;
+
+procedure TScreenPartyNewRound.SetAnimationProgress(Progress: real);
+begin
+ {Button[0].Texture.ScaleW := Progress;
+ Button[1].Texture.ScaleW := Progress;
+ Button[2].Texture.ScaleW := Progress; }
+end;
+
+end.
diff --git a/src/screens/UScreenPartyOptions.pas b/src/screens/UScreenPartyOptions.pas
new file mode 100644
index 00000000..f63b37fb
--- /dev/null
+++ b/src/screens/UScreenPartyOptions.pas
@@ -0,0 +1,284 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenPartyOptions;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UDisplay,
+ UMusic,
+ UFiles,
+ SysUtils,
+ UThemes;
+
+type
+ TScreenPartyOptions = class(TMenu)
+ private
+ SelectLevel: cardinal;
+ SelectPlayList: cardinal;
+ SelectPlayList2: cardinal;
+ SelectRounds: cardinal;
+
+ IPlaylist: array[0..2] of UTF8String;
+ IPlaylist2: array of UTF8String;
+
+ PlayList: integer;
+ PlayList2: integer;
+
+ procedure SetPlaylist2;
+ public
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UIni,
+ UTexture,
+ ULanguage,
+ UParty,
+ USong,
+ UPlaylist,
+ USongs,
+ UUnicodeUtils;
+
+function TScreenPartyOptions.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ I, J: integer;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenMain);
+ end;
+
+ SDLK_RETURN:
+ begin
+ //Don'T start when Playlist is Selected and there are no Playlists
+ if (Playlist = 2) and (Length(PlaylistMan.Playlists) = 0) then
+ Exit;
+
+ //Save Difficulty
+ Ini.Difficulty := SelectsS[SelectLevel].SelectedOption;
+ Ini.SaveLevel;
+
+ //Save Playlist
+ PlaylistMan.Mode := TSingMode( Playlist );
+ PlaylistMan.CurPlayList := High(cardinal);
+ //if Category Selected Search Category ID
+ if Playlist = 1 then
+ begin
+ J := -1;
+ for I := 0 to high(CatSongs.Song) do
+ begin
+ if CatSongs.Song[I].Main then
+ Inc(J);
+
+ if J = Playlist2 then
+ begin
+ PlaylistMan.CurPlayList := I;
+ Break;
+ end;
+ end;
+
+ //No Categorys or Invalid Entry
+ if PlaylistMan.CurPlayList = High(cardinal) then
+ Exit;
+ end
+ else
+ PlaylistMan.CurPlayList := Playlist2;
+
+ AudioPlayback.PlaySound(SoundLib.Start);
+ //Go to Player Screen
+ FadeTo(@ScreenPartyPlayer);
+ end;
+
+ // Up and Down could be done at the same time,
+ // but I don't want to declare variables inside
+ // functions like this one, called so many times
+ SDLK_DOWN: InteractNext;
+ SDLK_UP: InteractPrev;
+ SDLK_RIGHT:
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+
+ //Change Playlist2 if Playlist is Changed
+ if (Interaction = 1) then
+ begin
+ SetPlaylist2;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+
+ //Change Playlist2 if Playlist is Changed
+ if (Interaction = 1) then
+ begin
+ SetPlaylist2;
+ end;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenPartyOptions.Create;
+begin
+ inherited Create;
+ //Fill IPlaylist
+ IPlaylist[0] := Language.Translate('PARTY_PLAYLIST_ALL');
+ IPlaylist[1] := Language.Translate('PARTY_PLAYLIST_CATEGORY');
+ IPlaylist[2] := Language.Translate('PARTY_PLAYLIST_PLAYLIST');
+
+ //Fill IPlaylist2
+ SetLength(IPlaylist2, 1);
+ IPlaylist2[0] := '---';
+
+ //Clear all Selects
+ PlayList := 0;
+ PlayList2 := 0;
+
+ //Load Screen From Theme
+ LoadFromTheme(Theme.PartyOptions);
+
+ Theme.PartyOptions.SelectLevel.oneItemOnly := true;
+ Theme.PartyOptions.SelectLevel.showArrows := true;
+ SelectLevel := AddSelectSlide(Theme.PartyOptions.SelectLevel, Ini.Difficulty, Theme.ILevel);
+
+ Theme.PartyOptions.SelectPlayList.oneItemOnly := true;
+ Theme.PartyOptions.SelectPlayList.showArrows := true;
+ SelectPlayList := AddSelectSlide(Theme.PartyOptions.SelectPlayList, PlayList, IPlaylist);
+
+ Theme.PartyOptions.SelectPlayList2.oneItemOnly := true;
+ Theme.PartyOptions.SelectPlayList2.showArrows := true;
+ SelectPlayList2 := AddSelectSlide(Theme.PartyOptions.SelectPlayList2, PlayList2, IPlaylist2);
+
+ Interaction := 0;
+end;
+
+procedure TScreenPartyOptions.SetPlaylist2;
+var
+ I: integer;
+begin
+ case Playlist of
+ 0:
+ begin
+ SetLength(IPlaylist2, 1);
+ IPlaylist2[0] := '---';
+ end;
+ 1:
+ begin
+ SetLength(IPlaylist2, 0);
+ for I := 0 to high(CatSongs.Song) do
+ begin
+ if (CatSongs.Song[I].Main) then
+ begin
+ SetLength(IPlaylist2, Length(IPlaylist2) + 1);
+ IPlaylist2[high(IPlaylist2)] := CatSongs.Song[I].Artist;
+ end;
+ end;
+
+ if (Length(IPlaylist2) = 0) then
+ begin
+ SetLength(IPlaylist2, 1);
+ IPlaylist2[0] := 'No Categories found';
+ end;
+ end;
+ 2:
+ begin
+ if (Length(PlaylistMan.Playlists) > 0) then
+ begin
+ SetLength(IPlaylist2, Length(PlaylistMan.Playlists));
+ PlaylistMan.GetNames(IPlaylist2);
+ end
+ else
+ begin
+ SetLength(IPlaylist2, 1);
+ IPlaylist2[0] := 'No Playlists found';
+ end;
+ end;
+ end;
+
+ Playlist2 := 0;
+ UpdateSelectSlideOptions(Theme.PartyOptions.SelectPlayList2, 2, IPlaylist2, Playlist2);
+end;
+
+procedure TScreenPartyOptions.OnShow;
+begin
+ inherited;
+
+ Party.Clear;
+
+ // check if there are loaded modes
+ if Party.ModesAvailable then
+ begin
+ // modes are loaded
+ Randomize;
+ end
+ else
+ begin // no modes found
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_PLUGINS'));
+ Display.AbortScreenChange;
+ end;
+end;
+
+procedure TScreenPartyOptions.SetAnimationProgress(Progress: real);
+begin
+ {for I := 0 to 6 do
+ SelectS[I].Texture.ScaleW := Progress;}
+end;
+
+end.
diff --git a/src/screens/UScreenPartyPlayer.pas b/src/screens/UScreenPartyPlayer.pas
new file mode 100644
index 00000000..a7f4d627
--- /dev/null
+++ b/src/screens/UScreenPartyPlayer.pas
@@ -0,0 +1,449 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenPartyPlayer;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UDisplay,
+ UMusic,
+ UFiles,
+ SysUtils,
+ UThemes;
+
+type
+ TScreenPartyPlayer = class(TMenu)
+ private
+ CountTeams: integer;
+ CountPlayer: array [0..2] of integer;
+
+ SelectTeams: cardinal;
+ SelectPlayers: array [0..2] of cardinal;
+ procedure UpdateInterface;
+ procedure UpdateParty;
+ public
+ Team1Name: cardinal;
+ Player1Name: cardinal;
+ Player2Name: cardinal;
+ Player3Name: cardinal;
+ Player4Name: cardinal;
+
+ Team2Name: cardinal;
+ Player5Name: cardinal;
+ Player6Name: cardinal;
+ Player7Name: cardinal;
+ Player8Name: cardinal;
+
+ Team3Name: cardinal;
+ Player9Name: cardinal;
+ Player10Name: cardinal;
+ Player11Name: cardinal;
+ Player12Name: cardinal;
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+const
+ ITeams: array[0..1] of UTF8String = ('2', '3');
+ IPlayers: array[0..3] of UTF8String = ('1', '2', '3', '4');
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UIni,
+ UTexture,
+ UParty,
+ UUnicodeUtils,
+ UScreenPartyOptions,
+ ULanguage;
+
+procedure TScreenPartyPlayer.UpdateInterface;
+ var
+ I: integer;
+ Btn: integer;
+begin
+ SelectsS[SelectPlayers[2]].Visible := (CountTeams = 1);
+
+ Btn := 0;
+ for I := 0 to 2 do
+ begin
+ if (CountTeams + 1 >= I) then
+ begin
+ Button[Btn + 0].Visible := true;
+ Button[Btn + 1].Visible := (CountPlayer[I] + 1 >= 1);
+ Button[Btn + 2].Visible := (CountPlayer[I] + 1 >= 2);
+ Button[Btn + 3].Visible := (CountPlayer[I] + 1 >= 3);
+ Button[Btn + 4].Visible := (CountPlayer[I] + 1 >= 4);
+ end
+ else
+ begin
+ Button[Btn + 0].Visible := false;
+ Button[Btn + 1].Visible := false;
+ Button[Btn + 2].Visible := false;
+ Button[Btn + 3].Visible := false;
+ Button[Btn + 4].Visible := false;
+ end;
+ Inc(Btn, 5);
+ end;
+end;
+
+procedure TScreenPartyPlayer.UpdateParty;
+ var
+ I, J: integer;
+begin
+ {//Save PlayerNames
+ for I := 0 to PartySession.Teams.NumTeams-1 do
+ begin
+ PartySession.Teams.Teaminfo[I].Name := PChar(Button[I*5].Text[0].Text);
+ for J := 0 to PartySession.Teams.Teaminfo[I].NumPlayers-1 do
+ begin
+ PartySession.Teams.Teaminfo[I].Playerinfo[J].Name := PChar(Button[I*5 + J+1].Text[0].Text);
+ PartySession.Teams.Teaminfo[I].Playerinfo[J].TimesPlayed := 0;
+ end;
+ end; }
+
+ // add teams to party
+
+ for I := 0 to CountTeams + 1 do
+ begin
+ Party.AddTeam(Button[I * 5].Text[0].Text);
+
+ for J := 0 to CountPlayer[I] do
+ Party.AddPlayer(I, Button[I * 5 + 1 + J].Text[0].Text);
+ end;
+
+ if (Party.ModesAvailable) then
+ begin //mode for current playersetup available
+ FadeTo(@ScreenPartyRounds, SoundLib.Start);
+ end
+ else
+ begin
+ // no mode available for current player setup
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_MODES_FOR_CURRENT_SETUP'));
+ Party.Clear;
+ end;
+end;
+
+function TScreenPartyPlayer.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ SDL_ModState: word;
+ procedure IntNext;
+ begin
+ repeat
+ InteractNext;
+ until ((Interactions[Interaction].Typ = iSelectS) and
+ SelectsS[Interactions[Interaction].Num].Visible) or
+ (Button[Interactions[Interaction].Num].Visible);
+ end;
+ procedure IntPrev;
+ begin
+ repeat
+ InteractPrev;
+ until ((Interactions[Interaction].Typ = iSelectS) and
+ SelectsS[Interactions[Interaction].Num].Visible) or
+ (Button[Interactions[Interaction].Num].Visible);
+ end;
+begin
+ Result := true;
+
+ if (PressedDown) then
+ SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT
+ + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT)
+ else
+ SDL_ModState := 0;
+
+ // Key Down
+ // check normal keys
+ if (Interactions[Interaction].Typ = iButton) then
+ begin
+ case CharCode of
+ Ord('0')..Ord('9'),
+ Ord('a')..Ord('z'),
+ Ord('A')..Ord('Z'),
+ Ord(' '), Ord('-'), Ord('_'), Ord('!'), Ord(','), Ord('<'), Ord('/'),
+ Ord('*'), Ord('?'), Ord(''''), Ord('"'):
+ begin
+ Button[Interactions[Interaction].Num].Text[0].Text :=
+ Button[Interactions[Interaction].Num].Text[0].Text + UCS4ToUTF8String(CharCode);
+ Exit;
+ end;
+ end;
+
+
+ // check special keys
+ case PressedKey of
+ // Templates for Names Mod
+ SDLK_F1:
+ if (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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 (SDL_ModState = KMOD_LALT) 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_BACKSPACE:
+ begin
+ Button[Interactions[Interaction].Num].Text[0].DeleteLastLetter;
+ end;
+ end;
+ end;
+
+ case PressedKey of
+ SDLK_ESCAPE:
+ begin
+ Ini.SaveNames;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenPartyOptions);
+ end;
+
+ SDLK_RETURN: UpdateParty;
+
+ // Up and Down could be done at the same time,
+ // but I don't want to declare variables inside
+ // functions like this one, called so many times
+ SDLK_DOWN: IntNext;
+ SDLK_UP: IntPrev;
+ SDLK_RIGHT:
+ begin
+ if (Interaction in [0,2,8,14]) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+
+ UpdateInterface;
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ if (Interaction in [0,2,8,14]) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+
+ UpdateInterface;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenPartyPlayer.Create;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.PartyPlayer);
+
+ Theme.PartyPlayer.SelectTeams.oneItemOnly := true;
+ Theme.PartyPlayer.SelectTeams.showArrows := true;
+ SelectTeams := AddSelectSlide(Theme.PartyPlayer.SelectTeams, CountTeams, ITeams);
+
+ Team1Name := AddButton(Theme.PartyPlayer.Team1Name);
+ Theme.PartyPlayer.SelectPlayers1.oneItemOnly := true;
+ Theme.PartyPlayer.SelectPlayers1.showArrows := true;
+ SelectPlayers[0] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers1, CountPlayer[0], IPlayers);
+
+ AddButton(Theme.PartyPlayer.Player1Name);
+ AddButton(Theme.PartyPlayer.Player2Name);
+ AddButton(Theme.PartyPlayer.Player3Name);
+ AddButton(Theme.PartyPlayer.Player4Name);
+
+ Team2Name := AddButton(Theme.PartyPlayer.Team2Name);
+ Theme.PartyPlayer.SelectPlayers2.oneItemOnly := true;
+ Theme.PartyPlayer.SelectPlayers2.showArrows := true;
+ SelectPlayers[1] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers2, CountPlayer[1], IPlayers);
+
+ AddButton(Theme.PartyPlayer.Player5Name);
+ AddButton(Theme.PartyPlayer.Player6Name);
+ AddButton(Theme.PartyPlayer.Player7Name);
+ AddButton(Theme.PartyPlayer.Player8Name);
+
+ Team3Name := AddButton(Theme.PartyPlayer.Team3Name);
+ Theme.PartyPlayer.SelectPlayers3.oneItemOnly := true;
+ Theme.PartyPlayer.SelectPlayers3.showArrows := true;
+ SelectPlayers[2] := AddSelectSlide(Theme.PartyPlayer.SelectPlayers3, CountPlayer[2], IPlayers);
+
+ AddButton(Theme.PartyPlayer.Player9Name);
+ AddButton(Theme.PartyPlayer.Player10Name);
+ AddButton(Theme.PartyPlayer.Player11Name);
+ AddButton(Theme.PartyPlayer.Player12Name);
+
+ Interaction := 0;
+
+ //Clear Selects
+ CountTeams := 0;
+ CountPlayer[0] := 0;
+ CountPlayer[1] := 0;
+ CountPlayer[2] := 0;
+end;
+
+procedure TScreenPartyPlayer.OnShow;
+var
+ I: integer;
+begin
+ inherited;
+
+ // Templates for Names Mod
+ for I := 1 to 4 do
+ Button[I].Text[0].Text := Ini.Name[I-1];
+
+ for I := 6 to 9 do
+ Button[I].Text[0].Text := Ini.Name[I-2];
+
+ for I := 11 to 14 do
+ Button[I].Text[0].Text := Ini.Name[I-3];
+
+ Button[0].Text[0].Text := Ini.NameTeam[0];
+ Button[5].Text[0].Text := Ini.NameTeam[1];
+ Button[10].Text[0].Text := Ini.NameTeam[2];
+ // Templates for Names Mod end
+
+ Party.Clear;
+
+ UpdateInterface;
+end;
+
+procedure TScreenPartyPlayer.SetAnimationProgress(Progress: real);
+var
+ I: integer;
+begin
+ {for I := 0 to high(Button) do
+ Button[I].Texture.ScaleW := Progress; }
+end;
+
+end.
diff --git a/src/screens/UScreenPartyRounds.pas b/src/screens/UScreenPartyRounds.pas
new file mode 100644
index 00000000..070c9eb8
--- /dev/null
+++ b/src/screens/UScreenPartyRounds.pas
@@ -0,0 +1,233 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL: https://ultrastardx.svn.sourceforge.net/svnroot/ultrastardx/branches/experimental/Lua/src/screens/UScreenPartyOptions.pas $
+ * $Id: UScreenPartyOptions.pas 2036 2009-12-14 20:59:44Z whiteshark0 $
+ *}
+
+unit UScreenPartyRounds;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UDisplay,
+ UMusic,
+ UFiles,
+ SysUtils,
+ UThemes;
+
+type
+ TScreenPartyRounds = class(TMenu)
+ private
+ SelectRoundCount: cardinal;
+ SelectRound: array [0..6] of cardinal;
+
+ RoundCount: integer;
+ Round: array [0..6] of integer;
+
+ IModeNames: array of UTF8String;
+ IModeIDs: array of integer;
+
+ procedure UpdateInterface;
+ procedure StartParty;
+ public
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+const
+ IRoundCount: array[0..5] of UTF8String = ('2', '3', '4', '5', '6', '7');
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UIni,
+ UTexture,
+ ULanguage,
+ UParty,
+ USong,
+ UPlaylist,
+ USongs,
+ UUnicodeUtils;
+
+procedure TScreenPartyRounds.UpdateInterface;
+ var
+ I: integer;
+ ActualRounds: integer;
+begin
+ ActualRounds := RoundCount + 2;
+
+ for I := 0 to High(SelectRound) do
+ SelectsS[SelectRound[I]].Visible := (I < ActualRounds);
+end;
+
+procedure TScreenPartyRounds.StartParty;
+ var
+ GameRounds: ARounds;
+ I: integer;
+begin
+ SetLength(GameRounds, RoundCount + 2);
+
+ for I := 0 to High(GameRounds) do
+ GameRounds[I] := IModeIds[Round[I]];
+
+ // start party game
+ if (Party.StartGame(GameRounds)) then
+ begin
+ FadeTo(@ScreenPartyNewRound, SoundLib.Start);
+ end
+ else
+ begin
+ //error starting party game
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_CAN_NOT_START_PARTY'));
+ end;
+end;
+
+function TScreenPartyRounds.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenPartyPlayer);
+ end;
+
+ SDLK_RETURN: StartParty;
+
+ // Up and Down could be done at the same time,
+ // but I don't want to declare variables inside
+ // functions like this one, called so many times
+ SDLK_DOWN: InteractNext;
+ SDLK_UP: InteractPrev;
+ SDLK_RIGHT:
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractInc;
+
+ if Interaction = 0 then
+ UpdateInterface;
+ end;
+ SDLK_LEFT:
+ begin
+ AudioPlayback.PlaySound(SoundLib.Option);
+ InteractDec;
+
+ if Interaction = 0 then
+ UpdateInterface;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenPartyRounds.Create;
+ var
+ I: integer;
+begin
+ inherited Create;
+ RoundCount := 5;
+
+ //Load Screen From Theme
+ LoadFromTheme(Theme.PartyRounds);
+
+ Theme.PartyRounds.SelectRoundCount.oneItemOnly := true;
+ Theme.PartyRounds.SelectRoundCount.showArrows := true;
+ SelectRoundCount := AddSelectSlide(Theme.PartyRounds.SelectRoundCount, RoundCount, IRoundCount);
+
+ SetLength(IModeNames, 1);
+ IModeNames[0] := '---';
+ for I := 0 to high(Theme.PartyRounds.SelectRound) do
+ begin
+ Round[I] := 0;
+ Theme.PartyRounds.SelectRound[I].oneItemOnly := true;
+ Theme.PartyRounds.SelectRound[I].showArrows := true;
+ SelectRound[I] := AddSelectSlide(Theme.PartyRounds.SelectRound[I], Round[I], IModeNames);
+ end;
+
+
+ Interaction := 0;
+end;
+
+procedure TScreenPartyRounds.OnShow;
+ var
+ ModeList: AParty_ModeList;
+ I: integer;
+begin
+ inherited;
+
+ // check if there are loaded modes
+ if Party.ModesAvailable then
+ begin
+ UpdateInterface;
+
+ ModeList := Party.GetAvailableModes;
+ SetLength(IModeNames, Length(ModeList));
+ SetLength(IModeIds, Length(ModeList));
+ for I := 0 to High(ModeList) do
+ begin
+ IModeNames[I] := ModeList[I].Name;
+ IModeIds[I] := ModeList[I].Index;
+ end;
+
+ for I := 0 to High(SelectRound) do
+ UpdateSelectSlideOptions(Theme.PartyRounds.SelectRound[I] , SelectRound[I], IModeNames, Round[I]);
+ end
+ else
+ begin
+ // no mode available for current player setup
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_MODES_FOR_CURRENT_SETUP'));
+ Party.Clear;
+ Display.AbortScreenChange;
+ end;
+end;
+
+procedure TScreenPartyRounds.SetAnimationProgress(Progress: real);
+begin
+ {for I := 0 to 6 do
+ SelectS[I].Texture.ScaleW := Progress;}
+end;
+
+end.
diff --git a/src/screens/UScreenPartyScore.pas b/src/screens/UScreenPartyScore.pas
new file mode 100644
index 00000000..62c97161
--- /dev/null
+++ b/src/screens/UScreenPartyScore.pas
@@ -0,0 +1,334 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenPartyScore;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ SysUtils,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UThemes;
+
+type
+ TScreenPartyScore = class(TMenu)
+ public
+ TextScoreTeam1: cardinal;
+ TextScoreTeam2: cardinal;
+ TextScoreTeam3: cardinal;
+ TextNameTeam1: cardinal;
+ TextNameTeam2: cardinal;
+ TextNameTeam3: cardinal;
+ StaticTeam1: cardinal;
+ StaticTeam1BG: cardinal;
+ StaticTeam1Deco: cardinal;
+ StaticTeam2: cardinal;
+ StaticTeam2BG: cardinal;
+ StaticTeam2Deco: cardinal;
+ StaticTeam3: cardinal;
+ StaticTeam3BG: cardinal;
+ StaticTeam3Deco: cardinal;
+ TextWinner: cardinal;
+
+ DecoTex: array[0..5] of integer;
+ DecoColor: array[0..5] of Record
+ R, G, B: real;
+ end;
+
+ MaxScore: word;
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UParty,
+ ULanguage,
+ UTexture,
+ USkins,
+ UUnicodeUtils;
+
+function TScreenPartyScore.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE,
+ SDLK_RETURN :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+
+ Party.NextRound; //< go to next round
+
+ if (not Party.GameFinished) then
+ begin
+ FadeTo(@ScreenPartyNewRound);
+ end
+ else
+ begin
+ FadeTo(@ScreenPartyWin);
+ end;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenPartyScore.Create;
+var
+ Tex: TTexture;
+ R, G, B: real;
+ Color: integer;
+begin
+ inherited Create;
+
+ TextScoreTeam1 := AddText (Theme.PartyScore.TextScoreTeam1);
+ TextScoreTeam2 := AddText (Theme.PartyScore.TextScoreTeam2);
+ TextScoreTeam3 := AddText (Theme.PartyScore.TextScoreTeam3);
+ TextNameTeam1 := AddText (Theme.PartyScore.TextNameTeam1);
+ TextNameTeam2 := AddText (Theme.PartyScore.TextNameTeam2);
+ TextNameTeam3 := AddText (Theme.PartyScore.TextNameTeam3);
+
+ StaticTeam1 := AddStatic (Theme.PartyScore.StaticTeam1);
+ StaticTeam1BG := AddStatic (Theme.PartyScore.StaticTeam1BG);
+ StaticTeam1Deco := AddStatic (Theme.PartyScore.StaticTeam1Deco);
+ StaticTeam2 := AddStatic (Theme.PartyScore.StaticTeam2);
+ StaticTeam2BG := AddStatic (Theme.PartyScore.StaticTeam2BG);
+ StaticTeam2Deco := AddStatic (Theme.PartyScore.StaticTeam2Deco);
+ StaticTeam3 := AddStatic (Theme.PartyScore.StaticTeam3);
+ StaticTeam3BG := AddStatic (Theme.PartyScore.StaticTeam3BG);
+ StaticTeam3Deco := AddStatic (Theme.PartyScore.StaticTeam3Deco);
+
+ TextWinner := AddText (Theme.PartyScore.TextWinner);
+
+ //Load Deco Textures
+ if Theme.PartyScore.DecoTextures.ChangeTextures then
+ begin
+ //Get Color
+ LoadColor(R, G, B, Theme.PartyScore.DecoTextures.FirstColor);
+ Color := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255);
+ DecoColor[0].R := R;
+ DecoColor[0].G := G;
+ DecoColor[0].B := B;
+
+ //Load Texture
+ Tex := Texture.LoadTexture(
+ Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.FirstTexture),
+ Theme.PartyScore.DecoTextures.FirstTyp, Color);
+ DecoTex[0] := Tex.TexNum;
+
+ //Get Second Color
+ LoadColor(R, G, B, Theme.PartyScore.DecoTextures.SecondColor);
+ Color := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255);
+ DecoColor[1].R := R;
+ DecoColor[1].G := G;
+ DecoColor[1].B := B;
+
+ //Load Second Texture
+ Tex := Texture.LoadTexture(
+ Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.SecondTexture),
+ Theme.PartyScore.DecoTextures.SecondTyp, Color);
+ DecoTex[1] := Tex.TexNum;
+
+ //Get Third Color
+ LoadColor(R, G, B, Theme.PartyScore.DecoTextures.ThirdColor);
+ Color := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255);
+ DecoColor[2].R := R;
+ DecoColor[2].G := G;
+ DecoColor[2].B := B;
+
+ //Load Third Texture
+ Tex := Texture.LoadTexture(
+ Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.ThirdTexture),
+ Theme.PartyScore.DecoTextures.ThirdTyp, Color);
+ DecoTex[2] := Tex.TexNum;
+ end;
+
+ LoadFromTheme(Theme.PartyScore);
+end;
+
+procedure TScreenPartyScore.OnShow;
+var
+ Ranking: AParty_TeamRanking;
+begin
+ inherited;
+
+ // indicate that round is finished
+ Party.RoundPlayed;
+
+ // get rankings for current round
+ Ranking := Party.Rounds[Party.CurrentRound].Ranking;
+
+
+ {//Set Statics Length
+ Statics[StaticTeam1].Texture.ScaleW := ScreenSingModi.PlayerInfo.Playerinfo[0].Percentage / 100;
+ Statics[StaticTeam2].Texture.ScaleW := ScreenSingModi.PlayerInfo.Playerinfo[1].Percentage / 100;
+ Statics[StaticTeam3].Texture.ScaleW := ScreenSingModi.PlayerInfo.Playerinfo[2].Percentage / 100;
+
+ //fix: prevents statics from drawn out of bounds.
+ if Statics[StaticTeam1].Texture.ScaleW > 99 then Statics[StaticTeam1].Texture.ScaleW := 99;
+ if Statics[StaticTeam2].Texture.ScaleW > 99 then Statics[StaticTeam2].Texture.ScaleW := 99;
+ if Statics[StaticTeam3].Texture.ScaleW > 99 then Statics[StaticTeam3].Texture.ScaleW := 99; }
+
+ //Set Winnertext
+ Text[TextWinner].Text := Format(Language.Translate('PARTY_SCORE_WINS'), [Party.GetWinnerString(Party.CurrentRound)]);
+
+ if (Length(Party.Teams) >= 1) then
+ begin
+ Text[TextScoreTeam1].Text := InttoStr(Party.Teams[0].Score);
+ Text[TextNameTeam1].Text := Utf8String(Party.Teams[0].Name);
+
+ //Set Deco Texture
+ if Theme.PartyScore.DecoTextures.ChangeTextures then
+ begin
+ if (Length(Ranking) >= 1) and (Ranking[0].Rank >= 1) and (Ranking[0].Rank <= Length(DecoTex)) then
+ begin
+ Statics[StaticTeam1Deco].Texture.TexNum := DecoTex[Ranking[0].Rank-1];
+ Statics[StaticTeam1Deco].Texture.ColR := DecoColor[Ranking[0].Rank-1].R;
+ Statics[StaticTeam1Deco].Texture.ColG := DecoColor[Ranking[0].Rank-1].G;
+ Statics[StaticTeam1Deco].Texture.ColB := DecoColor[Ranking[0].Rank-1].B;
+ end;
+ end;
+
+ Text[TextScoreTeam1].Visible := true;
+ Text[TextNameTeam1].Visible := true;
+ Statics[StaticTeam1].Visible := true;
+ Statics[StaticTeam1BG].Visible := true;
+ Statics[StaticTeam1Deco].Visible := true;
+ end
+ else
+ begin
+ Text[TextScoreTeam1].Visible := false;
+ Text[TextNameTeam1].Visible := false;
+ Statics[StaticTeam1].Visible := false;
+ Statics[StaticTeam1BG].Visible := false;
+ Statics[StaticTeam1Deco].Visible := false;
+ end;
+
+ if (Length(Party.Teams) >= 2) then
+ begin
+ Text[TextScoreTeam2].Text := InttoStr(Party.Teams[1].Score);
+ Text[TextNameTeam2].Text := UTF8String(Party.Teams[1].Name);
+
+ //Set Deco Texture
+ if Theme.PartyScore.DecoTextures.ChangeTextures then
+ begin
+ if (Length(Ranking) >= 2) and (Ranking[1].Rank >= 1) and (Ranking[1].Rank <= Length(DecoTex)) then
+ begin
+ Statics[StaticTeam2Deco].Texture.TexNum := DecoTex[Ranking[1].Rank-1];
+ Statics[StaticTeam2Deco].Texture.ColR := DecoColor[Ranking[1].Rank-1].R;
+ Statics[StaticTeam2Deco].Texture.ColG := DecoColor[Ranking[1].Rank-1].G;
+ Statics[StaticTeam2Deco].Texture.ColB := DecoColor[Ranking[1].Rank-1].B;
+ end;
+ end;
+
+ Text[TextScoreTeam2].Visible := true;
+ Text[TextNameTeam2].Visible := true;
+ Statics[StaticTeam2].Visible := true;
+ Statics[StaticTeam2BG].Visible := true;
+ Statics[StaticTeam2Deco].Visible := true;
+ end
+ else
+ begin
+ Text[TextScoreTeam2].Visible := false;
+ Text[TextNameTeam2].Visible := false;
+ Statics[StaticTeam2].Visible := false;
+ Statics[StaticTeam2BG].Visible := false;
+ Statics[StaticTeam2Deco].Visible := false;
+ end;
+
+ if (Length(Party.Teams) >= 3) then
+ begin
+ Text[TextScoreTeam3].Text := InttoStr(Party.Teams[2].Score);
+ Text[TextNameTeam3].Text := UTF8String(Party.Teams[2].Name);
+
+ //Set Deco Texture
+ if Theme.PartyScore.DecoTextures.ChangeTextures then
+ begin
+ if (Length(Ranking) >= 3) and (Ranking[2].Rank >= 1) and (Ranking[2].Rank <= Length(DecoTex)) then
+ begin
+ Statics[StaticTeam3Deco].Texture.TexNum := DecoTex[Ranking[2].Rank-1];
+ Statics[StaticTeam3Deco].Texture.ColR := DecoColor[Ranking[2].Rank-1].R;
+ Statics[StaticTeam3Deco].Texture.ColG := DecoColor[Ranking[2].Rank-1].G;
+ Statics[StaticTeam3Deco].Texture.ColB := DecoColor[Ranking[2].Rank-1].B;
+ end;
+ end;
+
+ Text[TextScoreTeam3].Visible := true;
+ Text[TextNameTeam3].Visible := true;
+ Statics[StaticTeam3].Visible := true;
+ Statics[StaticTeam3BG].Visible := true;
+ Statics[StaticTeam3Deco].Visible := true;
+ end
+ else
+ begin
+ Text[TextScoreTeam3].Visible := false;
+ Text[TextNameTeam3].Visible := false;
+ Statics[StaticTeam3].Visible := false;
+ Statics[StaticTeam3BG].Visible := false;
+ Statics[StaticTeam3Deco].Visible := false;
+ end;
+
+ //start Background-Music
+ SoundLib.StartBgMusic;
+end;
+
+procedure TScreenPartyScore.SetAnimationProgress(Progress: real);
+begin
+ {if (ScreenSingModi.PlayerInfo.NumPlayers >= 1) then
+ Statics[StaticTeam1].Texture.ScaleW := Progress * ScreenSingModi.PlayerInfo.Playerinfo[0].Percentage / 100;
+ if (ScreenSingModi.PlayerInfo.NumPlayers >= 2) then
+ Statics[StaticTeam2].Texture.ScaleW := Progress * ScreenSingModi.PlayerInfo.Playerinfo[1].Percentage / 100;
+ if (ScreenSingModi.PlayerInfo.NumPlayers >= 3) then
+ Statics[StaticTeam3].Texture.ScaleW := Progress * ScreenSingModi.PlayerInfo.Playerinfo[2].Percentage / 100;}
+end;
+
+end.
diff --git a/src/screens/UScreenPartyWin.pas b/src/screens/UScreenPartyWin.pas
new file mode 100644
index 00000000..ed8d017c
--- /dev/null
+++ b/src/screens/UScreenPartyWin.pas
@@ -0,0 +1,295 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenPartyWin;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ SysUtils,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UThemes;
+
+type
+ TScreenPartyWin = class(TMenu)
+ public
+ TextScoreTeam1: cardinal;
+ TextScoreTeam2: cardinal;
+ TextScoreTeam3: cardinal;
+ TextNameTeam1: cardinal;
+ TextNameTeam2: cardinal;
+ TextNameTeam3: cardinal;
+ StaticTeam1: cardinal;
+ StaticTeam1BG: cardinal;
+ StaticTeam1Deco: cardinal;
+ StaticTeam2: cardinal;
+ StaticTeam2BG: cardinal;
+ StaticTeam2Deco: cardinal;
+ StaticTeam3: cardinal;
+ StaticTeam3BG: cardinal;
+ StaticTeam3Deco: cardinal;
+ TextWinner: cardinal;
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UParty,
+ ULanguage,
+ UUnicodeUtils;
+
+function TScreenPartyWin.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE,
+ SDLK_RETURN :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Start);
+ FadeTo(@ScreenMain);
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenPartyWin.Create;
+begin
+ inherited Create;
+
+ TextScoreTeam1 := AddText (Theme.PartyWin.TextScoreTeam1);
+ TextScoreTeam2 := AddText (Theme.PartyWin.TextScoreTeam2);
+ TextScoreTeam3 := AddText (Theme.PartyWin.TextScoreTeam3);
+ TextNameTeam1 := AddText (Theme.PartyWin.TextNameTeam1);
+ TextNameTeam2 := AddText (Theme.PartyWin.TextNameTeam2);
+ TextNameTeam3 := AddText (Theme.PartyWin.TextNameTeam3);
+
+ StaticTeam1 := AddStatic (Theme.PartyWin.StaticTeam1);
+ StaticTeam1BG := AddStatic (Theme.PartyWin.StaticTeam1BG);
+ StaticTeam1Deco := AddStatic (Theme.PartyWin.StaticTeam1Deco);
+ StaticTeam2 := AddStatic (Theme.PartyWin.StaticTeam2);
+ StaticTeam2BG := AddStatic (Theme.PartyWin.StaticTeam2BG);
+ StaticTeam2Deco := AddStatic (Theme.PartyWin.StaticTeam2Deco);
+ StaticTeam3 := AddStatic (Theme.PartyWin.StaticTeam3);
+ StaticTeam3BG := AddStatic (Theme.PartyWin.StaticTeam3BG);
+ StaticTeam3Deco := AddStatic (Theme.PartyWin.StaticTeam3Deco);
+
+ TextWinner := AddText (Theme.PartyWin.TextWinner);
+
+ LoadFromTheme(Theme.PartyWin);
+end;
+
+procedure TScreenPartyWin.OnShow;
+var
+ I: integer;
+ Ranking: AParty_TeamRanking;
+
+ Function GetTeamColor(Team: integer): cardinal;
+ var
+ NameString: string;
+ begin
+ NameString := 'P' + InttoStr(Team+1) + 'Dark';
+
+ Result := ColorExists(NameString);
+ end;
+
+begin
+ inherited;
+
+ // get team ranking
+ // Ranking is sorted by score
+ Ranking := Party.GetTeamRanking;
+
+ //Set Winnertext
+ Text[TextWinner].Text := Format(Language.Translate('PARTY_SCORE_WINS'), [Party.GetWinnerString(-1)]);
+ if (Length(Party.Teams) >= 1) then
+ begin
+ Text[TextScoreTeam1].Text := IntToStr(Party.Teams[Ranking[0].Team].Score);
+ Text[TextNameTeam1].Text := Party.Teams[Ranking[0].Team].Name;
+
+ Text[TextScoreTeam1].Visible := true;
+ Text[TextNameTeam1].Visible := true;
+ Statics[StaticTeam1].Visible := true;
+ Statics[StaticTeam1BG].Visible := true;
+ Statics[StaticTeam1Deco].Visible := true;
+
+ //Set Static Color to Team Color
+ if (Theme.PartyWin.StaticTeam1BG.Color = 'TeamColor') then
+ begin
+ I := GetTeamColor(Ranking[0].Team);
+ if (I <> -1) then
+ begin
+ Statics[StaticTeam1BG].Texture.ColR := Color[I].RGB.R;
+ Statics[StaticTeam1BG].Texture.ColG := Color[I].RGB.G;
+ Statics[StaticTeam1BG].Texture.ColB := Color[I].RGB.B;
+ end;
+ end;
+
+ if (Theme.PartyWin.StaticTeam1.Color = 'TeamColor') then
+ begin
+ I := GetTeamColor(Ranking[0].Team);
+ if (I <> -1) then
+ begin
+ Statics[StaticTeam1].Texture.ColR := Color[I].RGB.R;
+ Statics[StaticTeam1].Texture.ColG := Color[I].RGB.G;
+ Statics[StaticTeam1].Texture.ColB := Color[I].RGB.B;
+ end;
+ end;
+ end
+ else
+ begin
+ Text[TextScoreTeam1].Visible := false;
+ Text[TextNameTeam1].Visible := false;
+ Statics[StaticTeam1].Visible := false;
+ Statics[StaticTeam1BG].Visible := false;
+ Statics[StaticTeam1Deco].Visible := false;
+ end;
+
+ if (Length(Party.Teams) >= 2) then
+ begin
+ Text[TextScoreTeam2].Text := IntToStr(Party.Teams[Ranking[1].Team].Score);
+ Text[TextNameTeam2].Text := Party.Teams[Ranking[1].Team].Name;
+
+ Text[TextScoreTeam2].Visible := true;
+ Text[TextNameTeam2].Visible := true;
+ Statics[StaticTeam2].Visible := true;
+ Statics[StaticTeam2BG].Visible := true;
+ Statics[StaticTeam2Deco].Visible := true;
+
+ //Set Static Color to Team Color
+ if (Theme.PartyWin.StaticTeam2BG.Color = 'TeamColor') then
+ begin
+ I := GetTeamColor(Ranking[1].Team);
+ if (I <> -1) then
+ begin
+ Statics[StaticTeam2BG].Texture.ColR := Color[I].RGB.R;
+ Statics[StaticTeam2BG].Texture.ColG := Color[I].RGB.G;
+ Statics[StaticTeam2BG].Texture.ColB := Color[I].RGB.B;
+ end;
+ end;
+
+ if (Theme.PartyWin.StaticTeam2.Color = 'TeamColor') then
+ begin
+ I := GetTeamColor(Ranking[1].Team);
+ if (I <> -1) then
+ begin
+ Statics[StaticTeam2].Texture.ColR := Color[I].RGB.R;
+ Statics[StaticTeam2].Texture.ColG := Color[I].RGB.G;
+ Statics[StaticTeam2].Texture.ColB := Color[I].RGB.B;
+ end;
+ end;
+ end
+ else
+ begin
+ Text[TextScoreTeam2].Visible := false;
+ Text[TextNameTeam2].Visible := false;
+ Statics[StaticTeam2].Visible := false;
+ Statics[StaticTeam2BG].Visible := false;
+ Statics[StaticTeam2Deco].Visible := false;
+ end;
+
+ if (Length(Party.Teams) >= 3) then
+ begin
+ Text[TextScoreTeam3].Text := IntToStr(Party.Teams[Ranking[2].Team].Score);
+ Text[TextNameTeam3].Text := Party.Teams[Ranking[2].Team].Name;
+
+ Text[TextScoreTeam3].Visible := true;
+ Text[TextNameTeam3].Visible := true;
+ Statics[StaticTeam3].Visible := true;
+ Statics[StaticTeam3BG].Visible := true;
+ Statics[StaticTeam3Deco].Visible := true;
+
+ //Set Static Color to Team Color
+ if (Theme.PartyWin.StaticTeam3BG.Color = 'TeamColor') then
+ begin
+ I := GetTeamColor(Ranking[2].Team);
+ if (I <> -1) then
+ begin
+ Statics[StaticTeam3BG].Texture.ColR := Color[I].RGB.R;
+ Statics[StaticTeam3BG].Texture.ColG := Color[I].RGB.G;
+ Statics[StaticTeam3BG].Texture.ColB := Color[I].RGB.B;
+ end;
+ end;
+
+ if (Theme.PartyWin.StaticTeam3.Color = 'TeamColor') then
+ begin
+ I := GetTeamColor(Ranking[2].Team);
+ if (I <> -1) then
+ begin
+ Statics[StaticTeam3].Texture.ColR := Color[I].RGB.R;
+ Statics[StaticTeam3].Texture.ColG := Color[I].RGB.G;
+ Statics[StaticTeam3].Texture.ColB := Color[I].RGB.B;
+ end;
+ end;
+ end
+ else
+ begin
+ Text[TextScoreTeam3].Visible := false;
+ Text[TextNameTeam3].Visible := false;
+ Statics[StaticTeam3].Visible := false;
+ Statics[StaticTeam3BG].Visible := false;
+ Statics[StaticTeam3Deco].Visible := false;
+ end;
+end;
+
+procedure TScreenPartyWin.SetAnimationProgress(Progress: real);
+begin
+ {if (ScreenSingModi.PlayerInfo.NumPlayers >= 1) then
+ Statics[StaticTeam1].Texture.ScaleW := Progress * ScreenSingModi.PlayerInfo.Playerinfo[0].Score / maxScore;
+ if (ScreenSingModi.PlayerInfo.NumPlayers >= 2) then
+ Statics[StaticTeam2].Texture.ScaleW := Progress * ScreenSingModi.PlayerInfo.Playerinfo[1].Score / maxScore;
+ if (ScreenSingModi.PlayerInfo.NumPlayers >= 3) then
+ Statics[StaticTeam3].Texture.ScaleW := Progress * ScreenSingModi.PlayerInfo.Playerinfo[2].Score / maxScore;}
+end;
+
+end.
diff --git a/src/screens/UScreenPopup.pas b/src/screens/UScreenPopup.pas
new file mode 100644
index 00000000..fdf4a69c
--- /dev/null
+++ b/src/screens/UScreenPopup.pas
@@ -0,0 +1,308 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenPopup;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ SysUtils,
+ UMenu,
+ UMusic,
+ UFiles,
+ UThemes;
+
+type
+ TPopupCheckHandler = procedure(Value: boolean; Data: Pointer);
+
+ TScreenPopupCheck = class(TMenu)
+ private
+ fHandler: TPopupCheckHandler;
+ fHandlerData: Pointer;
+
+ public
+ Visible: boolean; // whether the menu should be drawn
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure ShowPopup(const Msg: UTF8String; Handler: TPopupCheckHandler;
+ HandlerData: Pointer; DefaultValue: boolean = false);
+ function Draw: boolean; override;
+ end;
+
+type
+ TScreenPopup = class(TMenu)
+ {
+ private
+ CurMenu: byte; //Num of the cur. Shown Menu
+ }
+ public
+ Visible: boolean; //Whether the Menu should be Drawn
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure OnHide; override;
+ procedure ShowPopup(const Msg: UTF8String);
+ function Draw: boolean; override;
+ end;
+
+ TScreenPopupError = class(TScreenPopup)
+ public
+ constructor Create;
+ end;
+
+ TScreenPopupInfo = class(TScreenPopup)
+ public
+ constructor Create;
+ end;
+
+var
+ //ISelections: array of string;
+ SelectValue: integer;
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UIni,
+ UTexture,
+ ULanguage,
+ UParty,
+ UPlaylist,
+ UDisplay,
+ UUnicodeUtils;
+
+{ TScreenPopupCheck }
+
+function TScreenPopupCheck.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ Value: boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ Value := false;
+ Visible := false;
+ Result := false;
+ end;
+
+ SDLK_RETURN:
+ begin
+ Value := (Interaction = 0);
+ Visible := false;
+ Result := false;
+ end;
+
+ SDLK_DOWN: InteractNext;
+ SDLK_UP: InteractPrev;
+
+ SDLK_RIGHT: InteractNext;
+ SDLK_LEFT: InteractPrev;
+ end;
+ end;
+
+ if (not Result) then
+ begin
+ if (@fHandler <> nil) then
+ fHandler(Value, fHandlerData);
+ end;
+end;
+
+constructor TScreenPopupCheck.Create;
+begin
+ inherited Create;
+
+ fHandler := nil;
+ fHandlerData := nil;
+
+ AddText(Theme.CheckPopup.TextCheck);
+
+ LoadFromTheme(Theme.CheckPopup);
+
+ AddButton(Theme.CheckPopup.Button1);
+ if (Length(Button[0].Text) = 0) then
+ AddButtonText(14, 20, 'Button 1');
+
+ AddButton(Theme.CheckPopup.Button2);
+ if (Length(Button[1].Text) = 0) then
+ AddButtonText(14, 20, 'Button 2');
+
+ Interaction := 0;
+end;
+
+function TScreenPopupCheck.Draw: boolean;
+begin
+ Result := inherited Draw;
+end;
+
+procedure TScreenPopupCheck.OnShow;
+begin
+ inherited;
+end;
+
+procedure TScreenPopupCheck.ShowPopup(const Msg: UTF8String; Handler: TPopupCheckHandler;
+ HandlerData: Pointer; DefaultValue: boolean);
+begin
+ if (DefaultValue) then
+ Interaction := 0
+ else
+ Interaction := 1;
+ Visible := true; //Set Visible
+ fHandler := Handler;
+ fHandlerData := HandlerData;
+
+ Text[0].Text := Language.Translate(msg);
+
+ Button[0].Visible := true;
+ Button[1].Visible := true;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_YES');
+ Button[1].Text[0].Text := Language.Translate('SONG_MENU_NO');
+
+ Background.OnShow
+end;
+
+{ TScreenPopup }
+
+function TScreenPopup.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+
+ case PressedKey of
+ SDLK_Q:
+ begin
+ Result := false;
+ end;
+
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ Visible := false;
+ Result := false;
+ end;
+
+ SDLK_RETURN:
+ begin
+ Visible := false;
+ Result := false;
+ end;
+
+ SDLK_DOWN: InteractNext;
+ SDLK_UP: InteractPrev;
+
+ SDLK_RIGHT: InteractNext;
+ SDLK_LEFT: InteractPrev;
+ end;
+ end;
+end;
+
+constructor TScreenPopup.Create;
+begin
+ inherited Create;
+
+ AddText(Theme.ErrorPopup.TextError);
+
+ LoadFromTheme(Theme.ErrorPopup);
+
+ AddButton(Theme.ErrorPopup.Button1);
+ if (Length(Button[0].Text) = 0) then
+ AddButtonText(14, 20, 'Button 1');
+
+ Interaction := 0;
+end;
+
+function TScreenPopup.Draw: boolean;
+begin
+ Draw := inherited Draw;
+end;
+
+procedure TScreenPopup.OnShow;
+begin
+ inherited;
+
+end;
+
+procedure TScreenPopup.OnHide;
+begin
+end;
+
+procedure TScreenPopup.ShowPopup(const Msg: UTF8String);
+begin
+ Interaction := 0; //Reset Interaction
+ Visible := true; //Set Visible
+ Background.OnShow;
+
+{ //dirty hack... Text[0] is invisible for some strange reason
+ for i:=1 to high(Text) do
+ if i-1 <= high(msg) then
+ begin
+ Text[i].Visible := true;
+ Text[i].Text := msg[i-1];
+ end
+ else
+ begin
+ Text[i].Visible := false;
+ end;}
+ Text[0].Text := msg;
+
+ Button[0].Visible := true;
+
+ Button[0].Text[0].Text := 'OK';
+end;
+
+{ TScreenPopupError }
+
+constructor TScreenPopupError.Create;
+begin
+ inherited;
+ Text[1].Text := Language.Translate('MSG_ERROR_TITLE');
+end;
+
+{ TScreenPopupInfo }
+
+constructor TScreenPopupInfo.Create;
+begin
+ inherited;
+ Text[1].Text := Language.Translate('MSG_INFO_TITLE');
+end;
+
+end.
diff --git a/src/screens/UScreenScore.pas b/src/screens/UScreenScore.pas
new file mode 100644
index 00000000..de7675bf
--- /dev/null
+++ b/src/screens/UScreenScore.pas
@@ -0,0 +1,1187 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenScore;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ SysUtils,
+ UDisplay,
+ UMusic,
+ USongs,
+ UThemes,
+ gl,
+ math,
+ UTexture;
+
+const
+ ZBars: real = 0.8; // Z value for the bars
+ ZRatingPic: real = 0.8; // Z value for the rating pictures
+
+ EaseOut_MaxSteps: real = 10; // that's the speed of the bars (10 is fast | 100 is slower)
+
+ BarRaiseSpeed: cardinal = 14; // Time for raising the bar one step higher (in ms)
+
+type
+ TScoreBarType = (sbtScore, sbtLine, sbtGolden);
+ TPlayerScoreScreenTexture = record // holds all colorized textures for up to 6 players
+ //Bar textures
+ Score_NoteBarLevel_Dark: TTexture; // Note
+ Score_NoteBarRound_Dark: TTexture; // that's the round thing on top
+
+ Score_NoteBarLevel_Light: TTexture; // LineBonus | Phrasebonus
+ Score_NoteBarRound_Light: TTexture;
+
+ Score_NoteBarLevel_Lightest: TTexture; // GoldenNotes
+ Score_NoteBarRound_Lightest: TTexture;
+
+ Player_Id_Box: TTexture; // boxes with player numbers
+ end;
+
+ TPlayerScoreScreenData = record // holds the positions and other data
+ Bar_Y: real;
+ Bar_Actual_Height: real; // this one holds the actual height of the bar, while we animate it
+ BarScore_ActualHeight: real;
+ BarLine_ActualHeight: real;
+ BarGolden_ActualHeight: real;
+ end;
+
+ TPlayerScoreRatingPics = record // a fine array of the rating pictures
+ RateEaseStep: integer;
+ RateEaseValue: real;
+ end;
+
+ { 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)
+ Screen: byte; // 0 - Screen 1; 1 - Screen 2
+ BothScreens: boolean; // true if player is drawn on both screens
+ end;
+ APlayerPositionMap = array of TPlayerPositionMap;
+
+ { textures for playerstatics of seconds screen players }
+ TPlayerStaticTexture = record
+ Tex: TTexture;
+ end;
+
+ TScreenScore = class(TMenu)
+ private
+ { holds position and screen of players(index)
+ set by calling MapPlayerstoPosition() }
+ PlayerPositionMap: APlayerPositionMap;
+
+ BarTime: cardinal;
+
+ aPlayerScoreScreenTextures: array[1..6] of TPlayerScoreScreenTexture;
+ aPlayerScoreScreenDatas: array[1..6] of TPlayerScoreScreenData;
+ aPlayerScoreScreenRatings: array[1..6] of TPlayerScoreRatingPics;
+
+ BarScore_EaseOut_Step: real;
+ BarPhrase_EaseOut_Step: real;
+ BarGolden_EaseOut_Step: real;
+
+ TextArtist: integer;
+ TextTitle: integer;
+
+ TextArtistTitle: integer;
+
+ TextName: array[1..6] of integer;
+ TextScore: array[1..6] of integer;
+
+ TextNotes: array[1..6] of integer;
+ TextNotesScore: array[1..6] of integer;
+ TextLineBonus: array[1..6] of integer;
+ TextLineBonusScore: array[1..6] of integer;
+ TextGoldenNotes: array[1..6] of integer;
+ TextGoldenNotesScore: array[1..6] of integer;
+ TextTotal: array[1..6] of integer;
+ TextTotalScore: array[1..6] of integer;
+
+ PlayerStatic: array[1..6] of array 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..6] of array of array [1..2] of TPlayerStaticTexture;
+ PlayerTexts: array[1..6] of array of integer;
+
+ StaticBoxLightest: array[1..6] of integer;
+ StaticBoxLight: array[1..6] of integer;
+ StaticBoxDark: array[1..6] 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..6] of array[0..2] of array [1..2] of TPlayerStaticTexture;
+
+ StaticBackLevel: array[1..6] of integer;
+ StaticBackLevelRound: array[1..6] of integer;
+ StaticLevel: array[1..6] of integer;
+ StaticLevelRound: array[1..6] of integer;
+
+ { statics with players ids }
+ StaticPlayerIdBox: array[1..6] of integer;
+ TexturePlayerIdBox: array[1..6] of TTexture;
+
+ Animation: real;
+
+ TextScore_ActualValue: array[1..6] of integer;
+ TextPhrase_ActualValue: array[1..6] of integer;
+ TextGolden_ActualValue: array[1..6] of integer;
+
+ procedure MapPlayersToPosition;
+
+ procedure FillPlayer(Item, P: integer);
+ procedure FillPlayerItems(PlayerNumber: integer);
+
+ procedure UpdateAnimation;
+ {****
+ * helpers for bar easing
+ *}
+ procedure EaseBarIn(PlayerNumber: integer; BarType: TScoreBarType);
+ procedure EaseScoreIn(PlayerNumber: integer; ScoreType: TScoreBarType);
+
+ procedure DrawPlayerBars;
+
+ procedure DrawBar(BarType: TScoreBarType; PlayerNumber: integer; BarStartPosY: single; NewHeight: real);
+
+ {****
+ * helpers for rating picture
+ *}
+ procedure ShowRating(PlayerNumber: integer);
+ function CalculateBouncing(PlayerNumber: integer): real;
+ procedure DrawRating(PlayerNumber: integer; Rating: integer);
+
+ { for player static texture swapping }
+ procedure LoadSwapTextures;
+ procedure SwapToScreen(Screen: integer);
+ public
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ function ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: integer): boolean; override;
+ procedure OnShow; override;
+ procedure OnShowFinish; override;
+ function Draw: boolean; override;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UScreenSong,
+ UMenuStatic,
+ UTime,
+ UIni,
+ USkins,
+ ULog,
+ ULanguage,
+ UNote,
+ UUnicodeUtils;
+
+
+function TScreenScore.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE,
+ SDLK_RETURN:
+ begin
+ FadeTo(@ScreenTop5);
+ Exit;
+ end;
+
+ SDLK_SYSREQ:
+ begin
+ Display.SaveScreenShot;
+ end;
+ end;
+ end;
+end;
+
+function TScreenScore.ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: integer): boolean;
+begin
+ Result := True;
+ if (MouseButton = SDL_BUTTON_LEFT) and BtnDown then begin
+ //left-click anywhere sends return
+ ParseInput(SDLK_RETURN, 0, true);
+ end;
+end;
+
+procedure TScreenScore.LoadSwapTextures;
+ var
+ P, I: integer;
+ PlayerNum, PlayerNum2: integer;
+ Color: string;
+ R, G, B: real;
+ 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
+ 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;
+ 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
+ 0: begin
+ StaticNum := StaticBoxLightest[P];
+ ThemeStatic := Theme.Score.StaticBoxLightest[P];
+ end;
+ 1: begin
+ StaticNum := StaticBoxLight[P];
+ ThemeStatic := Theme.Score.StaticBoxLight[P];
+ end;
+ 2: begin
+ StaticNum := StaticBoxDark[P];
+ 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
+ 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;
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure TScreenScore.SwapToScreen(Screen: integer);
+ var
+ P, I: integer;
+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;
+
+ { set correct box textures }
+ for I := 0 to High(PlayerPositionMap) do
+ begin
+ if (PlayerPositionMap[I].Position > 0) and ((ScreenAct = PlayerPositionMap[I].Screen) or (PlayerPositionMap[I].BothScreens)) then
+ begin
+ // we just set the texture specific stuff
+ // so we don't overwrite e.g. width and height
+ with Statics[StaticPlayerIdBox[PlayerPositionMap[I].Position]].Texture do
+ begin
+ TexNum := aPlayerScoreScreenTextures[I+1].Player_Id_Box.TexNum;
+ TexW := aPlayerScoreScreenTextures[I+1].Player_Id_Box.TexW;
+ TexH := aPlayerScoreScreenTextures[I+1].Player_Id_Box.TexH;
+ end;
+ end;
+ end;
+
+ if (Screens = 2) then
+ begin
+ { 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;
+ end;
+
+ { box statics }
+ for P := Low(PlayerStatic) to High(PlayerStatic) 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;
+ end;
+ end;
+end;
+
+constructor TScreenScore.Create;
+var
+ Player: integer;
+ Counter: integer;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.Score);
+
+ // These two texts arn't used in the deluxe skin
+ TextArtist := AddText(Theme.Score.TextArtist);
+ TextTitle := AddText(Theme.Score.TextTitle);
+
+ TextArtistTitle := AddText(Theme.Score.TextArtistTitle);
+
+ for Player := 1 to 6 do
+ begin
+ SetLength(PlayerStatic[Player], Length(Theme.Score.PlayerStatic[Player]));
+ SetLength(PlayerTexts[Player], Length(Theme.Score.PlayerTexts[Player]));
+
+ for Counter := 0 to High(Theme.Score.PlayerStatic[Player]) do
+ PlayerStatic[Player, Counter] := AddStatic(Theme.Score.PlayerStatic[Player, Counter]);
+
+
+
+ for Counter := 0 to High(Theme.Score.PlayerTexts[Player]) do
+ PlayerTexts[Player, Counter] := AddText(Theme.Score.PlayerTexts[Player, Counter]);
+
+ TextName[Player] := AddText(Theme.Score.TextName[Player]);
+ TextScore[Player] := AddText(Theme.Score.TextScore[Player]);
+
+ TextNotes[Player] := AddText(Theme.Score.TextNotes[Player]);
+ TextNotesScore[Player] := AddText(Theme.Score.TextNotesScore[Player]);
+ TextLineBonus[Player] := AddText(Theme.Score.TextLineBonus[Player]);
+ TextLineBonusScore[Player] := AddText(Theme.Score.TextLineBonusScore[Player]);
+ TextGoldenNotes[Player] := AddText(Theme.Score.TextGoldenNotes[Player]);
+ TextGoldenNotesScore[Player] := AddText(Theme.Score.TextGoldenNotesScore[Player]);
+ TextTotal[Player] := AddText(Theme.Score.TextTotal[Player]);
+ TextTotalScore[Player] := AddText(Theme.Score.TextTotalScore[Player]);
+
+ StaticBoxLightest[Player] := AddStatic(Theme.Score.StaticBoxLightest[Player]);
+ StaticBoxLight[Player] := AddStatic(Theme.Score.StaticBoxLight[Player]);
+ StaticBoxDark[Player] := AddStatic(Theme.Score.StaticBoxDark[Player]);
+
+ StaticBackLevel[Player] := AddStatic(Theme.Score.StaticBackLevel[Player]);
+ StaticBackLevelRound[Player] := AddStatic(Theme.Score.StaticBackLevelRound[Player]);
+ StaticLevel[Player] := AddStatic(Theme.Score.StaticLevel[Player]);
+ StaticLevelRound[Player] := AddStatic(Theme.Score.StaticLevelRound[Player]);
+ StaticPlayerIdBox[Player] := AddStatic(Theme.Score.StaticPlayerIdBox[Player]);
+
+ //textures
+ aPlayerScoreScreenTextures[Player].Score_NoteBarLevel_Dark := Tex_Score_NoteBarLevel_Dark[Player];
+ aPlayerScoreScreenTextures[Player].Score_NoteBarRound_Dark := Tex_Score_NoteBarRound_Dark[Player];
+
+ aPlayerScoreScreenTextures[Player].Score_NoteBarLevel_Light := Tex_Score_NoteBarLevel_Light[Player];
+ aPlayerScoreScreenTextures[Player].Score_NoteBarRound_Light := Tex_Score_NoteBarRound_Light[Player];
+
+ aPlayerScoreScreenTextures[Player].Score_NoteBarLevel_Lightest := Tex_Score_NoteBarLevel_Lightest[Player];
+ aPlayerScoreScreenTextures[Player].Score_NoteBarRound_Lightest := Tex_Score_NoteBarRound_Lightest[Player];
+ aPlayerScoreScreenTextures[Player].Player_Id_Box := Texture.GetTexture(Skin.GetTextureFileName('PlayerIDBox0' + IntToStr(Player)), Texture_Type_Transparent);
+ end;
+
+ LoadSwapTextures;
+end;
+
+procedure TScreenScore.MapPlayersToPosition;
+ var
+ ArrayStartModifier: integer;
+ PlayersPerScreen: integer;
+ I: 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) then
+ PlayersPerScreen := PlayersPlay
+ else
+ PlayersPerScreen := PlayersPlay div 2;
+
+ SetLength(PlayerPositionMap, PlayersPlay);
+
+ // actually map players to positions
+ 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;
+
+procedure TScreenScore.UpdateAnimation;
+var
+ CurrentTime: integer;
+ I: integer;
+begin
+ CurrentTime := SDL_GetTicks();
+
+ if (ScreenAct = 1) and ShowFinish then
+ while (CurrentTime >= BarTime) do
+ begin
+ Inc(BarTime, BarRaiseSpeed);
+
+ // We actually arise them in the right order, but we have to draw them in reverse order (golden -> phrase -> mainscore)
+ if (BarScore_EaseOut_Step < EaseOut_MaxSteps * 10) then
+ BarScore_EaseOut_Step:= BarScore_EaseOut_Step + 1
+
+ // PhrasenBonus
+ else if (BarPhrase_EaseOut_Step < EaseOut_MaxSteps * 10) then
+ BarPhrase_EaseOut_Step := BarPhrase_EaseOut_Step + 1
+
+ // GoldenNotebonus
+ else if (BarGolden_EaseOut_Step < EaseOut_MaxSteps * 10) then
+ BarGolden_EaseOut_Step := BarGolden_EaseOut_Step + 1
+
+ // rating icon
+ else
+ for I := 1 to PlayersPlay do
+ CalculateBouncing(I);
+ end;
+end;
+
+procedure TScreenScore.DrawPlayerBars;
+ var
+ I: integer;
+begin
+ for I := 0 to PlayersPlay-1 do
+ begin
+ if (PlayerPositionMap[I].Position > 0) and ((ScreenAct = PlayerPositionMap[I].Screen) or (PlayerPositionMap[I].BothScreens)) then
+ begin
+ if (BarScore_EaseOut_Step >= (EaseOut_MaxSteps * 10)) then
+ begin
+ if (BarPhrase_EaseOut_Step >= (EaseOut_MaxSteps * 10)) then
+ begin
+ // Draw golden score bar #
+ EaseBarIn(I + 1, sbtGolden);
+ EaseScoreIn(I + 1, sbtGolden);
+ end;
+
+ // Draw phrase score bar #
+ EaseBarIn(I + 1, sbtLine);
+ EaseScoreIn(I + 1, sbtLine);
+ end;
+
+ // Draw plain score bar #
+ EaseBarIn(I + 1, sbtScore);
+ EaseScoreIn(I + 1, sbtScore);
+ end;
+ end;
+end;
+
+procedure TScreenScore.OnShow;
+var
+ P: integer; // player
+ I: integer;
+ V: array[1..6] of boolean; // visibility array
+
+begin
+
+ {**
+ * Turn backgroundmusic on
+ *}
+ SoundLib.StartBgMusic;
+
+ inherited;
+
+ MapPlayersToPosition;
+
+ for P := 1 to PlayersPlay do
+ begin
+ // data
+ aPlayerScoreScreenDatas[P].Bar_Y := Theme.Score.StaticBackLevel[PlayerPositionMap[P-1].Position].Y;
+
+ // ratings
+ aPlayerScoreScreenRatings[P].RateEaseStep := 1;
+ aPlayerScoreScreenRatings[P].RateEaseValue := 20;
+
+ // actual values
+ TextScore_ActualValue[P] := 0;
+ TextPhrase_ActualValue[P] := 0;
+ TextGolden_ActualValue[P] := 0;
+ end;
+
+ Text[TextArtist].Text := CurrentSong.Artist;
+ Text[TextTitle].Text := CurrentSong.Title;
+ Text[TextArtistTitle].Text := CurrentSong.Artist + ' - ' + CurrentSong.Title;
+
+ // 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
+ V[1] := false;
+ V[2] := true;
+ V[3] := true;
+ V[4] := false;
+ V[5] := false;
+ V[6] := false;
+ end;
+ 3, 6: begin
+ V[1] := false;
+ V[2] := false;
+ V[3] := false;
+ V[4] := true;
+ V[5] := true;
+ V[6] := true;
+ end;
+ end;
+
+ for P := 1 to 6 do
+ begin
+ Text[TextName[P]].Visible := V[P];
+ Text[TextScore[P]].Visible := V[P];
+
+ // 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;
+
+ 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];
+
+ Statics[StaticPlayerIdBox[P]].Visible := V[P];
+
+ // we draw that on our own
+ Statics[StaticBackLevel[P]].Visible := false;
+ Statics[StaticBackLevelRound[P]].Visible := false;
+ Statics[StaticLevel[P]].Visible := false;
+ Statics[StaticLevelRound[P]].Visible := false;
+ end;
+
+ BarScore_EaseOut_Step := 1;
+ BarPhrase_EaseOut_Step := 1;
+ BarGolden_EaseOut_Step := 1;
+end;
+
+procedure TScreenScore.onShowFinish;
+var
+ index: integer;
+begin
+ for index := 1 to (PlayersPlay) do
+ begin
+ TextScore_ActualValue[index] := 0;
+ TextPhrase_ActualValue[index] := 0;
+ TextGolden_ActualValue[index] := 0;
+ end;
+
+ BarTime := SDL_GetTicks();
+end;
+
+function TScreenScore.Draw: boolean;
+var
+ PlayerCounter: integer;
+begin
+{*
+ player[0].ScoreInt := 7000;
+ player[0].ScoreLineInt := 2000;
+ player[0].ScoreGoldenInt := 1000;
+ player[0].ScoreTotalInt := 10000;
+
+ player[1].ScoreInt := 2500;
+ player[1].ScoreLineInt := 1100;
+ player[1].ScoreGoldenInt := 900;
+ player[1].ScoreTotalInt := 4500;
+//*}
+ // swap static textures to current screen ones
+ SwapToScreen(ScreenAct);
+
+ //Draw the Background
+ DrawBG;
+
+ // Let's start to arise the bars
+ UpdateAnimation;
+
+ // we have to swap the themeobjects values on every draw
+ // to support dual screen
+ for PlayerCounter := 1 to PlayersPlay do
+ begin
+ FillPlayerItems(PlayerCounter);
+ end;
+
+ if (ShowFinish) then
+ DrawPlayerBars;
+
+ //Draw Theme Objects
+ DrawFG;
+
+(*
+ //todo: i need a clever method to draw statics with their z value
+ for I := 0 to Length(Statics) - 1 do
+ Statics[I].Draw;
+ for I := 0 to Length(Text) - 1 do
+ Text[I].Draw;
+*)
+
+ Result := true;
+end;
+
+procedure TscreenScore.FillPlayerItems(PlayerNumber: integer);
+var
+ ThemeIndex: integer;
+begin
+ ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position;
+ if (ThemeIndex > 0) and ((ScreenAct = PlayerPositionMap[PlayerNumber-1].Screen) or (PlayerPositionMap[PlayerNumber-1].BothScreens)) then
+ begin
+ // todo: take the name from player[PlayerNumber].Name instead of the ini when this is done (mog)
+ Text[TextName[ThemeIndex]].Text := Ini.Name[PlayerNumber-1];
+ // end todo
+
+ //golden
+ Text[TextGoldenNotesScore[ThemeIndex]].Text := IntToStr(TextGolden_ActualValue[PlayerNumber]);
+ Text[TextGoldenNotesScore[ThemeIndex]].Alpha := (BarGolden_EaseOut_Step / 100);
+
+ Statics[StaticBoxLightest[ThemeIndex]].Texture.Alpha := (BarGolden_EaseOut_Step / 100);
+ Text[TextGoldenNotes[ThemeIndex]].Alpha := (BarGolden_EaseOut_Step / 100);
+
+ // line bonus
+ Text[TextLineBonusScore[ThemeIndex]].Text := IntToStr(TextPhrase_ActualValue[PlayerNumber]);
+ Text[TextLineBonusScore[ThemeIndex]].Alpha := (BarPhrase_EaseOut_Step / 100);
+
+ Statics[StaticBoxLight[ThemeIndex]].Texture.Alpha := (BarPhrase_EaseOut_Step / 100);
+ Text[TextLineBonus[ThemeIndex]].Alpha := (BarPhrase_EaseOut_Step / 100);
+
+ // plain score
+ Text[TextNotesScore[ThemeIndex]].Text := IntToStr(TextScore_ActualValue[PlayerNumber]);
+ Text[TextNotes[ThemeIndex]].Alpha := (BarScore_EaseOut_Step / 100);
+
+ Statics[StaticBoxDark[ThemeIndex]].Texture.Alpha := (BarScore_EaseOut_Step / 100);
+ Text[TextNotesScore[ThemeIndex]].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[TextTotal[ThemeIndex]].Alpha := (BarScore_EaseOut_Step / 100);
+
+ if(BarGolden_EaseOut_Step = 100) then
+ begin
+ ShowRating(PlayerNumber);
+ end;
+ end;
+end;
+
+procedure TScreenScore.ShowRating(PlayerNumber: integer);
+var
+ Rating: integer;
+ ThemeIndex: integer;
+begin
+ ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position;
+ if (ThemeIndex > 0) and ((ScreenAct = PlayerPositionMap[PlayerNumber-1].Screen) or (PlayerPositionMap[PlayerNumber-1].BothScreens)) then
+ begin
+ case (Player[PlayerNumber-1].ScoreTotalInt) of
+ 0..2009:
+ begin
+ Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_TONE_DEAF');
+ Rating := 0;
+ end;
+ 2010..4009:
+ begin
+ Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_AMATEUR');
+ Rating := 1;
+ end;
+ 4010..5009:
+ begin
+ Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_WANNABE');
+ Rating := 2;
+ end;
+ 5010..6009:
+ begin
+ Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_HOPEFUL');
+ Rating := 3;
+ end;
+ 6010..7509:
+ begin
+ Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_RISING_STAR');
+ Rating := 4;
+ end;
+ 7510..8509:
+ begin
+ Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_LEAD_SINGER');
+ Rating := 5;
+ end;
+ 8510..9009:
+ begin
+ Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_SUPERSTAR');
+ Rating := 6;
+ end;
+ 9010..10000:
+ begin
+ Text[TextScore[ThemeIndex]].Text := Language.Translate('SING_SCORE_ULTRASTAR');
+ Rating := 7;
+ end;
+ else
+ Rating := 0; // Cheata :P
+ 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
+ begin
+ Text[TextScore[ThemeIndex]].Alpha := aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue / Theme.Score.StaticRatings[ThemeIndex].W;
+ end;
+ // end todo
+
+ DrawRating(PlayerNumber, Rating);
+ end;
+end;
+
+procedure TscreenScore.DrawRating(PlayerNumber: integer; Rating: integer);
+var
+ Posx: real;
+ Posy: real;
+ Width: real;
+ ThemeIndex: integer;
+begin
+ ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position;
+
+ 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); ;
+
+ Width := aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue/2;
+
+ glBindTexture(GL_TEXTURE_2D, Tex_Score_Ratings[Rating].TexNum);
+
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 0); glVertex2f(PosX - Width, PosY - Width);
+ glTexCoord2f(Tex_Score_Ratings[Rating].TexW, 0); glVertex2f(PosX + Width, PosY - Width);
+ glTexCoord2f(Tex_Score_Ratings[Rating].TexW, Tex_Score_Ratings[Rating].TexH); glVertex2f(PosX + Width, PosY + Width);
+ glTexCoord2f(0, Tex_Score_Ratings[Rating].TexH); glVertex2f(PosX - Width, PosY + Width);
+ glEnd;
+
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2d);
+end;
+
+function TscreenScore.CalculateBouncing(PlayerNumber: integer): real;
+var
+ p, s: real;
+
+ RaiseStep, MaxVal: real;
+ EaseOut_Step: integer;
+ ThemeIndex: integer;
+begin
+ ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position;
+
+ EaseOut_Step := aPlayerScoreScreenRatings[PlayerNumber].RateEaseStep;
+ MaxVal := Theme.Score.StaticRatings[ThemeIndex].W;
+
+ RaiseStep := EaseOut_Step;
+
+ if (MaxVal > 0) and (RaiseStep > 0) then
+ RaiseStep := RaiseStep / MaxVal;
+
+ if (RaiseStep = 1) then
+ begin
+ Result := MaxVal;
+ end
+ else
+ begin
+ p := MaxVal * 0.4;
+
+ s := p/(2*PI) * arcsin (1);
+ Result := MaxVal * power(2,-5 * RaiseStep) * sin( (RaiseStep * MaxVal - s) * (2 * PI) / p) + MaxVal;
+
+ inc(aPlayerScoreScreenRatings[PlayerNumber].RateEaseStep);
+ aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue := Result;
+ end;
+end;
+
+procedure TscreenScore.EaseBarIn(PlayerNumber: integer; BarType: TScoreBarType);
+const
+ RaiseSmoothness: integer = 100;
+var
+ MaxHeight: real;
+ NewHeight: real;
+
+ Height2Reach: real;
+ RaiseStep: real;
+ BarStartPosY: single;
+
+ lTmp: real;
+ Score: integer;
+ ThemeIndex: integer;
+begin
+ ThemeIndex := PlayerPositionMap[PlayerNumber-1].Position;
+ MaxHeight := Theme.Score.StaticBackLevel[ThemeIndex].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]
+ // EaseOut_Step is the actual step in the raising process, like the 20iest step of EaseOut_MaxSteps
+ if (BarType = sbtScore) then
+ begin
+ Score := Player[PlayerNumber - 1].ScoreInt;
+ RaiseStep := BarScore_EaseOut_Step;
+ BarStartPosY := Theme.Score.StaticBackLevel[ThemeIndex].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;
+ 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;
+ end;
+
+ // the height dependend of the score
+ Height2Reach := (Score / MAX_SONG_SCORE) * MaxHeight;
+
+ if (aPlayerScoreScreenDatas[PlayerNumber].Bar_Actual_Height < Height2Reach) then
+ begin
+ // Check http://proto.layer51.com/d.aspx?f=400 for more info on easing functions
+ // Calculate the actual step according to the maxsteps
+ RaiseStep := RaiseStep / EaseOut_MaxSteps;
+
+ // quadratic easing out - decelerating to zero velocity
+ // -end_position * current_time * ( current_time - 2 ) + start_postion
+ lTmp := (-Height2Reach * RaiseStep * (RaiseStep - 20) + BarStartPosY);
+
+ if ( RaiseSmoothness > 0 ) and ( lTmp > 0 ) then
+ NewHeight := lTmp / RaiseSmoothness;
+
+ end
+ else
+ NewHeight := Height2Reach;
+
+ DrawBar(BarType, PlayerNumber, BarStartPosY, NewHeight);
+
+ if (BarType = sbtScore) then
+ aPlayerScoreScreenDatas[PlayerNumber].BarScore_ActualHeight := NewHeight
+ else if (BarType = sbtLine) then
+ aPlayerScoreScreenDatas[PlayerNumber].BarLine_ActualHeight := NewHeight
+ else if (BarType = sbtGolden) then
+ aPlayerScoreScreenDatas[PlayerNumber].BarGolden_ActualHeight := NewHeight;
+end;
+
+procedure TscreenScore.DrawBar(BarType: TScoreBarType; PlayerNumber: integer; BarStartPosY: single; NewHeight: real);
+var
+ Width: real;
+ BarStartPosX: real;
+ ThemeIndex: integer;
+begin
+ ThemeIndex := 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;
+
+ glColor4f(1, 1, 1, 1);
+
+ // set the texture for the bar
+ if (BarType = sbtScore) then
+ glBindTexture(GL_TEXTURE_2D, aPlayerScoreScreenTextures[PlayerNumber].Score_NoteBarLevel_Dark.TexNum);
+ if (BarType = sbtLine) then
+ glBindTexture(GL_TEXTURE_2D, aPlayerScoreScreenTextures[PlayerNumber].Score_NoteBarLevel_Light.TexNum);
+ if (BarType = sbtGolden) then
+ glBindTexture(GL_TEXTURE_2D, aPlayerScoreScreenTextures[PlayerNumber].Score_NoteBarLevel_Lightest.TexNum);
+
+ //draw it
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 0); glVertex3f(BarStartPosX, BarStartPosY - NewHeight, ZBars);
+ glTexCoord2f(1, 0); glVertex3f(BarStartPosX + Width, BarStartPosY - NewHeight, ZBars);
+ glTexCoord2f(1, 1); glVertex3f(BarStartPosX + Width, BarStartPosY, ZBars);
+ glTexCoord2f(0, 1); glVertex3f(BarStartPosX, BarStartPosY, ZBars);
+ glEnd;
+
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2d);
+
+ //the round thing on top
+ if (BarType = sbtScore) then
+ glBindTexture(GL_TEXTURE_2D, aPlayerScoreScreenTextures[PlayerNumber].Score_NoteBarRound_Dark.TexNum);
+ if (BarType = sbtLine) then
+ glBindTexture(GL_TEXTURE_2D, aPlayerScoreScreenTextures[PlayerNumber].Score_NoteBarRound_Light.TexNum);
+ if (BarType = sbtGolden) then
+ glBindTexture(GL_TEXTURE_2D, aPlayerScoreScreenTextures[PlayerNumber].Score_NoteBarRound_Lightest.TexNum);
+
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ 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(1, 1); glVertex3f(BarStartPosX + Width, BarStartPosY - NewHeight, ZBars);
+ glTexCoord2f(0, 1); glVertex3f(BarStartPosX, BarStartPosY - NewHeight, ZBars);
+ glEnd;
+
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2d);
+end;
+
+procedure TScreenScore.EaseScoreIn(PlayerNumber: integer; ScoreType: TScoreBarType);
+const
+ RaiseSmoothness: integer = 100;
+var
+ RaiseStep: real;
+ lTmpA: real;
+ ScoreReached: integer;
+ EaseOut_Step: real;
+ ActualScoreValue: integer;
+begin
+ if (ScoreType = sbtScore) then
+ begin
+ EaseOut_Step := BarScore_EaseOut_Step;
+ ActualScoreValue := TextScore_ActualValue[PlayerNumber];
+ ScoreReached := Player[PlayerNumber-1].ScoreInt;
+ end;
+ if (ScoreType = sbtLine) then
+ begin
+ EaseOut_Step := BarPhrase_EaseOut_Step;
+ ActualScoreValue := TextPhrase_ActualValue[PlayerNumber];
+ ScoreReached := Player[PlayerNumber-1].ScoreLineInt;
+ end;
+ if (ScoreType = sbtGolden) then
+ begin
+ EaseOut_Step := BarGolden_EaseOut_Step;
+ ActualScoreValue := TextGolden_ActualValue[PlayerNumber];
+ ScoreReached := Player[PlayerNumber-1].ScoreGoldenInt;
+ end;
+
+ // EaseOut_Step is the actual step in the raising process, like the 20iest step of EaseOut_MaxSteps
+ RaiseStep := EaseOut_Step;
+
+ if (ActualScoreValue < ScoreReached) then
+ begin
+ // Calculate the actual step according to the maxsteps
+ RaiseStep := RaiseStep / EaseOut_MaxSteps;
+
+ // quadratic easing out - decelerating to zero velocity
+ // -end_position * current_time * ( current_time - 2 ) + start_postion
+ lTmpA := (-ScoreReached * RaiseStep * (RaiseStep - 20));
+ if ( lTmpA > 0 ) and
+ ( RaiseSmoothness > 0 ) then
+ begin
+ if (ScoreType = sbtScore) then
+ TextScore_ActualValue[PlayerNumber] := floor( lTmpA / RaiseSmoothness);
+ if (ScoreType = sbtLine) then
+ TextPhrase_ActualValue[PlayerNumber] := floor( lTmpA / RaiseSmoothness);
+ if (ScoreType = sbtGolden) then
+ TextGolden_ActualValue[PlayerNumber] := floor( lTmpA / RaiseSmoothness);
+ end;
+ end
+ else
+ begin
+ if (ScoreType = sbtScore) then
+ TextScore_ActualValue[PlayerNumber] := ScoreReached;
+ if (ScoreType = sbtLine) then
+ TextPhrase_ActualValue[PlayerNumber] := ScoreReached;
+ if (ScoreType = sbtGolden) then
+ TextGolden_ActualValue[PlayerNumber] := ScoreReached;
+ end;
+end;
+
+procedure TScreenScore.FillPlayer(Item, P: integer);
+var
+ S: string;
+begin
+ Text[TextName[Item]].Text := Ini.Name[P];
+
+ S := IntToStr((Round(Player[P].Score) div 10) * 10);
+ while (Length(S)<4) do
+ S := '0' + S;
+ Text[TextNotesScore[Item]].Text := S;
+
+ // while (Length(S)<5) do S := '0' + S;
+ // Text[TextTotalScore[Item]].Text := S;
+
+ //fixed: line bonus and golden notes don't show up,
+ // another bug: total score was shown without added golden-, linebonus
+ S := IntToStr(Player[P].ScoreTotalInt);
+ while (Length(S)<5) do
+ S := '0' + S;
+ Text[TextTotalScore[Item]].Text := S;
+
+ S := IntToStr(Player[P].ScoreLineInt);
+ while (Length(S)<4) do
+ S := '0' + S;
+ Text[TextLineBonusScore[Item]].Text := S;
+
+ S := IntToStr(Player[P].ScoreGoldenInt);
+ while (Length(S)<4) do
+ S := '0' + S;
+ Text[TextGoldenNotesScore[Item]].Text := S;
+ //end of fix
+
+end;
+
+end.
diff --git a/src/screens/UScreenSing.pas b/src/screens/UScreenSing.pas
new file mode 100644
index 00000000..3b8fda40
--- /dev/null
+++ b/src/screens/UScreenSing.pas
@@ -0,0 +1,1071 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenSing;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SysUtils,
+ SDL,
+ TextGL,
+ gl,
+ UFiles,
+ UGraphicClasses,
+ UIni,
+ ULog,
+ ULyrics,
+ UMenu,
+ UMusic,
+ USingScores,
+ USongs,
+ UTexture,
+ UThemes,
+ UPath,
+ UTime,
+ UHookableEvent;
+
+type
+ TLyricsSyncSource = class(TSyncSource)
+ function GetClock(): real; override;
+ end;
+
+ TMusicSyncSource = class(TSyncSource)
+ function GetClock(): real; override;
+ end;
+
+ TTimebarMode = (
+ tbmCurrent, // current song position
+ tbmRemaining, // remaining time
+ tbmTotal // total time
+ );
+
+type
+ TScreenSing = class(TMenu)
+ private
+ fShowVisualization: boolean;
+ fCurrentVideo: IVideo;
+ fVideoClip: IVideo;
+ fLyricsSync: TLyricsSyncSource;
+ fMusicSync: TMusicSyncSource;
+ fTimebarMode: TTimebarMode;
+ protected
+ eSongLoaded: THookableEvent; //< event is called after lyrics of a song are loaded on OnShow
+ Paused: boolean; //pause Mod
+ NumEmptySentences: integer;
+ public
+ // timebar fields
+ StaticTimeProgress: integer;
+ TextTimeText: integer;
+
+ StaticP1: integer;
+ TextP1: integer;
+
+ // shown when game is in 2/4 player modus
+ StaticP1TwoP: integer;
+ TextP1TwoP: integer;
+
+ // shown when game is in 3/6 player modus
+ StaticP1ThreeP: integer;
+ TextP1ThreeP: integer;
+
+ StaticP2R: integer;
+ TextP2R: integer;
+
+ StaticP2M: integer;
+ TextP2M: integer;
+
+ StaticP3R: integer;
+ TextP3R: integer;
+
+ StaticPausePopup: integer;
+
+ Tex_Background: TTexture;
+ FadeOut: boolean;
+ Lyrics: TLyricEngine;
+
+ // score manager:
+ Scores: TSingScores;
+
+ //the song was sung to the end
+ SungToEnd: boolean;
+
+ // some settings to be set by plugins
+ Settings: record
+ Finish: Boolean; //< if true, screen will finish on next draw
+
+ LyricsVisible: Boolean; //< shows or hides lyrics
+ NotesVisible: Integer; //< if bit[playernum] is set the notes for the specified player are visible. By default all players notes are visible
+
+ PlayerEnabled: Integer; //< defines whether a player can score atm
+ end;
+ procedure ClearSettings;
+ procedure ApplySettings; //< applies changes of settings record
+ procedure EndSong;
+
+ constructor Create; override;
+ procedure OnShow; override;
+ procedure OnShowFinish; override;
+ procedure OnHide; override;
+
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char;
+ PressedDown: boolean): boolean; override;
+ function Draw: boolean; override;
+
+ procedure Finish; virtual;
+ procedure Pause; // toggle pause
+
+ procedure OnSentenceEnd(SentenceIndex: cardinal); // for linebonus + singbar
+ procedure OnSentenceChange(SentenceIndex: cardinal); // for golden notes
+ end;
+
+implementation
+
+uses
+ Classes,
+ Math,
+ UDraw,
+ UGraphic,
+ ULanguage,
+ UNote,
+ URecord,
+ USong,
+ UDisplay,
+ UParty,
+ UUnicodeUtils;
+
+// method for input parsing. if false is returned, getnextwindow
+// should be checked to know the next window to load;
+
+function TScreenSing.ParseInput(PressedKey: Cardinal; CharCode: UCS4Char;
+ PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // key down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ // when not ask before exit then finish now
+ if (Ini.AskbeforeDel <> 1) then
+ Finish
+ // else just pause and let the popup make the work
+ else if not Paused then
+ Pause;
+
+ Result := false;
+ Exit;
+ end;
+
+ // show visualization
+ Ord('V'):
+ begin
+ fShowVisualization := not fShowVisualization;
+
+ if fShowVisualization then
+ begin
+ fCurrentVideo := Visualization.Open(PATH_NONE);
+ fCurrentVideo.play;
+ end
+ else
+ begin
+ fCurrentVideo := fVideoClip;
+ end;
+ Exit;
+ end;
+
+ // pause
+ Ord('P'):
+ begin
+ Pause;
+ Exit;
+ end;
+
+ // toggle time display
+ Ord('T'):
+ begin
+ if (fTimebarMode = High(TTimebarMode)) then
+ fTimebarMode := Low(TTimebarMode)
+ else
+ Inc(fTimebarMode);
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE:
+ begin
+ // record sound hack:
+ //Sound[0].BufferLong
+
+ Finish;
+ FadeOut := true;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ end;
+
+ SDLK_SPACE:
+ begin
+ Pause;
+ end;
+
+ SDLK_TAB: // change visualization preset
+ begin
+ if fShowVisualization then
+ fCurrentVideo.Position := now; // move to a random position
+ end;
+
+ SDLK_RETURN:
+ begin
+ end;
+
+ // up and down could be done at the same time,
+ // but i don't want to declare variables inside
+ // functions like this one, called so many times
+ SDLK_DOWN:
+ begin
+ end;
+ SDLK_UP:
+ begin
+ end;
+ end;
+ end;
+end;
+
+// pause mod
+procedure TScreenSing.Pause;
+begin
+ if (not Paused) then // enable pause
+ begin
+ // pause time
+ Paused := true;
+
+ LyricsState.Pause();
+
+ // pause music
+ AudioPlayback.Pause;
+
+ // pause video
+ if (fCurrentVideo <> nil) then
+ fCurrentVideo.Pause;
+
+ end
+ else // disable pause
+ begin
+ LyricsState.Start();
+
+ // play music
+ AudioPlayback.Play;
+
+ // video
+ if (fCurrentVideo <> nil) then
+ fCurrentVideo.Pause;
+
+ Paused := false;
+ end;
+end;
+// pause mod end
+
+constructor TScreenSing.Create;
+begin
+ inherited Create;
+
+ //too dangerous, a mouse button is quickly pressed by accident
+ RightMbESC := false;
+
+ fShowVisualization := false;
+
+ fCurrentVideo := nil;
+
+ // create score class
+ Scores := TSingScores.Create;
+ Scores.LoadfromTheme;
+
+ LoadFromTheme(Theme.Sing);
+
+ // timebar
+ StaticTimeProgress := AddStatic(Theme.Sing.StaticTimeProgress);
+ TextTimeText := AddText(Theme.Sing.TextTimeText);
+
+ // 1 player | P1
+ StaticP1 := AddStatic(Theme.Sing.StaticP1);
+ TextP1 := AddText(Theme.Sing.TextP1);
+
+ // 2 or 4 players | P1
+ StaticP1TwoP := AddStatic(Theme.Sing.StaticP1TwoP);
+ TextP1TwoP := AddText(Theme.Sing.TextP1TwoP);
+
+ // | P2
+ StaticP2R := AddStatic(Theme.Sing.StaticP2R);
+ TextP2R := AddText(Theme.Sing.TextP2R);
+
+ // 3 or 6 players | P1
+ StaticP1ThreeP := AddStatic(Theme.Sing.StaticP1ThreeP);
+ TextP1ThreeP := AddText(Theme.Sing.TextP1ThreeP);
+
+ // | P2
+ StaticP2M := AddStatic(Theme.Sing.StaticP2M);
+ TextP2M := AddText(Theme.Sing.TextP2M);
+
+ // | P3
+ StaticP3R := AddStatic(Theme.Sing.StaticP3R);
+ TextP3R := AddText(Theme.Sing.TextP3R);
+
+ StaticPausePopup := AddStatic(Theme.Sing.PausePopUp);
+
+ // <note> pausepopup is not visibile at the beginning </note>
+ Statics[StaticPausePopup].Visible := false;
+
+ Lyrics := TLyricEngine.Create(
+ Theme.LyricBar.UpperX, Theme.LyricBar.UpperY, Theme.LyricBar.UpperW, Theme.LyricBar.UpperH,
+ Theme.LyricBar.LowerX, Theme.LyricBar.LowerY, Theme.LyricBar.LowerW, Theme.LyricBar.LowerH);
+
+ fLyricsSync := TLyricsSyncSource.Create();
+ fMusicSync := TMusicSyncSource.Create();
+
+ eSongLoaded := THookableEvent.Create('ScreenSing.SongLoaded');
+
+ ClearSettings;
+end;
+
+procedure TScreenSing.OnShow;
+var
+ Index: integer;
+ 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;
+ Color: TRGB;
+ VideoFile, BgFile: IPath;
+ success: boolean;
+ BadPlayer: integer;
+begin
+ inherited;
+
+ Log.LogStatus('Begin', 'OnShow');
+ FadeOut := false;
+
+ //the song was sung to the end
+ SungToEnd := false;
+ ClearSettings;
+ Party.CallBeforeSing;
+
+ // reset video playback engine
+ fCurrentVideo := nil;
+
+ // setup score manager
+ Scores.ClearPlayers; // clear old player values
+ Color.R := 0;
+ Color.G := 0;
+ Color.B := 0; // dummy atm <- \(O.o)/? B like bummy?
+
+ // add new players
+ for Index := 0 to PlayersPlay - 1 do
+ begin
+ Scores.AddPlayer(Tex_ScoreBG[Index], Color);
+ end;
+
+ Scores.Init; // get positions for players
+
+ // prepare players
+ SetLength(Player, PlayersPlay);
+
+ case PlayersPlay of
+ 1:
+ begin
+ V1 := true;
+ V1TwoP := false;
+ V1ThreeP := false;
+ V2R := false;
+ V2M := false;
+ V3R := false;
+ end;
+ 2:
+ begin
+ V1 := false;
+ V1TwoP := true;
+ V1ThreeP := false;
+ V2R := true;
+ V2M := false;
+ V3R := false;
+ end;
+ 3:
+ begin
+ V1 := false;
+ V1TwoP := false;
+ V1ThreeP := true;
+ V2R := false;
+ V2M := true;
+ V3R := true;
+ end;
+ 4:
+ begin // double screen
+ V1 := false;
+ V1TwoP := true;
+ V1ThreeP := false;
+ V2R := true;
+ V2M := false;
+ V3R := false;
+ end;
+ 6:
+ begin // double screen
+ V1 := false;
+ V1TwoP := false;
+ V1ThreeP := true;
+ V2R := false;
+ V2M := true;
+ V3R := true;
+ end;
+
+ end;
+
+ // this one is shown in 1P mode
+ Statics[StaticP1].Visible := V1;
+ Text[TextP1].Visible := V1;
+
+ // this one is shown in 2/4P mode
+ Statics[StaticP1TwoP].Visible := V1TwoP;
+ Text[TextP1TwoP].Visible := V1TwoP;
+
+ Statics[StaticP2R].Visible := V2R;
+ Text[TextP2R].Visible := V2R;
+
+ // this one is shown in 3/6P mode
+ Statics[StaticP1ThreeP].Visible := V1ThreeP;
+ Text[TextP1ThreeP].Visible := V1ThreeP;
+
+ Statics[StaticP2M].Visible := V2M;
+ Text[TextP2M].Visible := V2M;
+
+ Statics[StaticP3R].Visible := V3R;
+ Text[TextP3R].Visible := V3R;
+
+ fTimebarMode := tbmCurrent;
+
+ // FIXME: sets path and filename to ''
+ ResetSingTemp;
+
+ CurrentSong := CatSongs.Song[CatSongs.Selected];
+
+ // FIXME: bad style, put the try-except into loadsong() and not here
+ try
+ // check if file is xml
+ if CurrentSong.FileName.GetExtension.ToUTF8 = '.xml' then
+ success := CurrentSong.AnalyseXML and CurrentSong.LoadXMLSong()
+ else
+ success := CurrentSong.Analyse and CurrentSong.LoadSong();
+ except
+ success := false;
+ end;
+
+ if (not success) then
+ begin
+ // error loading song -> go back to previous screen and show some error message
+ Display.AbortScreenChange;
+ // select new song in party mode
+ if ScreenSong.Mode = smPartyMode then
+ ScreenSong.SelectRandomSong();
+ if (Length(CurrentSong.LastError) > 0) then
+ ScreenPopupError.ShowPopup(Format(Language.Translate(CurrentSong.LastError), [CurrentSong.ErrorLineNo]))
+ else
+ ScreenPopupError.ShowPopup(Language.Translate('ERROR_CORRUPT_SONG'));
+ // FIXME: do we need this?
+ CurrentSong.Path := CatSongs.Song[CatSongs.Selected].Path;
+ Exit;
+ end;
+
+ {*
+ * == Background ==
+ * We have four types of backgrounds:
+ * + Blank : Nothing has been set, this is our fallback
+ * + Picture : Picture has been set, and exists - otherwise we fallback
+ * + Video : Video has been set, and exists - otherwise we fallback
+ * + Visualization: + Off : No visualization
+ * + WhenNoVideo: Overwrites blank and picture
+ * + On : Overwrites blank, picture and video
+ *}
+
+ {*
+ * set background to: video
+ *}
+ fShowVisualization := false;
+ VideoFile := CurrentSong.Path.Append(CurrentSong.Video);
+ if (Ini.VideoEnabled = 1) and CurrentSong.Video.IsSet() and VideoFile.IsFile then
+ begin
+ fVideoClip := VideoPlayback.Open(VideoFile);
+ fCurrentVideo := fVideoClip;
+ if (fVideoClip <> nil) then
+ begin
+ fShowVisualization := false;
+ fCurrentVideo.Position := CurrentSong.VideoGAP + CurrentSong.Start;
+ fCurrentVideo.Play;
+ end;
+ end;
+
+ {*
+ * set background to: picture
+ *}
+ if (CurrentSong.Background.IsSet) and (fVideoClip = nil)
+ and (TVisualizerOption(Ini.VisualizerOption) = voOff) then
+ begin
+ BgFile := CurrentSong.Path.Append(CurrentSong.Background);
+ try
+ Tex_Background := Texture.LoadTexture(BgFile);
+ except
+ Log.LogError('Background could not be loaded: ' + BgFile.ToNative);
+ Tex_Background.TexNum := 0;
+ end
+ end
+ else
+ begin
+ Tex_Background.TexNum := 0;
+ end;
+
+ {*
+ * set background to: visualization (Overwrites all)
+ *}
+ if (TVisualizerOption(Ini.VisualizerOption) in [voOn]) then
+ begin
+ fShowVisualization := true;
+ fCurrentVideo := Visualization.Open(PATH_NONE);
+ if (fCurrentVideo <> nil) then
+ fCurrentVideo.Play;
+ end;
+
+ {*
+ * set background to: visualization (Videos are still shown)
+ *}
+ if ((TVisualizerOption(Ini.VisualizerOption) in [voWhenNoVideo]) and
+ (fVideoClip = nil)) then
+ begin
+ fShowVisualization := true;
+ fCurrentVideo := Visualization.Open(PATH_NONE);
+ if (fCurrentVideo <> nil) then
+ fCurrentVideo.Play;
+ end;
+
+ // prepare lyrics timer
+ LyricsState.Reset();
+ LyricsState.SetCurrentTime(CurrentSong.Start);
+ LyricsState.StartTime := CurrentSong.Gap;
+ if (CurrentSong.Finish > 0) then
+ LyricsState.TotalTime := CurrentSong.Finish / 1000
+ else
+ LyricsState.TotalTime := AudioPlayback.Length;
+ LyricsState.UpdateBeats();
+
+ BadPlayer := AudioInputProcessor.CheckPlayersConfig(PlayersPlay);
+ if (BadPlayer <> 0) then
+ begin
+ ScreenPopupError.ShowPopup(
+ Format(Language.Translate('ERROR_PLAYER_NO_DEVICE_ASSIGNMENT'),
+ [BadPlayer]));
+ end;
+
+ // prepare and start voice-capture
+ AudioInput.CaptureStart;
+
+ // clear the scores of all players
+
+ for Index := 0 to High(Player) do
+ with Player[Index] do
+ begin
+ Score := 0;
+ ScoreLine := 0;
+ ScoreGolden := 0;
+
+ ScoreInt := 0;
+ ScoreLineInt := 0;
+ ScoreGoldenInt := 0;
+ ScoreTotalInt := 0;
+
+ ScoreLast := 0;
+
+ LastSentencePerfect := false;
+ end;
+
+ // main text
+ Lyrics.Clear(CurrentSong.BPM[0].BPM, CurrentSong.Resolution);
+
+ // set custom options
+ case Ini.LyricsFont of
+ 0: // normal fonts
+ begin
+ Lyrics.FontStyle := ftNormal;
+
+ Lyrics.LineColor_en.R := Skin_FontR;
+ Lyrics.LineColor_en.G := Skin_FontG;
+ Lyrics.LineColor_en.B := Skin_FontB;
+ Lyrics.LineColor_en.A := 1;
+
+ Lyrics.LineColor_dis.R := 0.4;
+ Lyrics.LineColor_dis.G := 0.4;
+ Lyrics.LineColor_dis.B := 0.4;
+ Lyrics.LineColor_dis.A := 1;
+
+ Lyrics.LineColor_act.R := 0.02;
+ Lyrics.LineColor_act.G := 0.6;
+ Lyrics.LineColor_act.B := 0.8;
+ Lyrics.LineColor_act.A := 1;
+ end;
+ 1, 2: // outline fonts
+ begin
+ if (Ini.LyricsFont = 1) then
+ Lyrics.FontStyle := ftOutline1
+ else
+ Lyrics.FontStyle := ftOutline2;
+
+ Lyrics.LineColor_en.R := 0.75;
+ Lyrics.LineColor_en.G := 0.75;
+ Lyrics.LineColor_en.B := 1;
+ Lyrics.LineColor_en.A := 1;
+
+ Lyrics.LineColor_dis.R := 0.8;
+ Lyrics.LineColor_dis.G := 0.8;
+ Lyrics.LineColor_dis.B := 0.8;
+ Lyrics.LineColor_dis.A := 1;
+
+ Lyrics.LineColor_act.R := 0.5;
+ Lyrics.LineColor_act.G := 0.5;
+ Lyrics.LineColor_act.B := 1;
+ Lyrics.LineColor_act.A := 1;
+ end;
+ end; // case
+
+ // initialize lyrics by filling its queue
+ while (not Lyrics.IsQueueFull) and
+ (Lyrics.LineCounter <= High(Lines[0].Line)) do
+ begin
+ Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]);
+ end;
+
+ // deactivate pause
+ Paused := false;
+
+ // kill all stars not killed yet (goldenstarstwinkle mod)
+ GoldenRec.SentenceChange;
+
+ // set position of line bonus - line bonus end
+ // set number of empty sentences for line bonus
+ NumEmptySentences := 0;
+ for Index := Low(Lines[0].Line) to High(Lines[0].Line) do
+ if Lines[0].Line[Index].TotalNotes = 0 then
+ Inc(NumEmptySentences);
+
+ eSongLoaded.CallHookChain(False);
+
+ Log.LogStatus('End', 'OnShow');
+end;
+
+procedure TScreenSing.onShowFinish;
+begin
+ // hide cursor on singscreen show
+ Display.SetCursor;
+
+ // prepare music
+ // Important: AudioPlayback must not be initialized in onShow() as TScreenSong
+ // uses stops AudioPlayback in onHide() which interferes with TScreenSings onShow.
+ AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3));
+ AudioPlayback.SetVolume(1.0);
+ AudioPlayback.Position := CurrentSong.Start;
+
+ // synchronize music
+ if (Ini.SyncTo = Ord(stLyrics)) then
+ AudioPlayback.SetSyncSource(fLyricsSync)
+ else
+ AudioPlayback.SetSyncSource(nil);
+
+ // synchronize lyrics (do not set this before AudioPlayback is initialized)
+ if (Ini.SyncTo = Ord(stMusic)) then
+ LyricsState.SetSyncSource(fMusicSync)
+ else
+ LyricsState.SetSyncSource(nil);
+
+ // start lyrics
+ LyricsState.Start(true);
+
+ // start music
+ AudioPlayback.Play();
+
+ // start timer
+ CountSkipTimeSet;
+end;
+
+procedure TScreenSing.ClearSettings;
+begin
+ Settings.Finish := False;
+ Settings.LyricsVisible := True;
+ Settings.NotesVisible := high(Integer);
+ Settings.PlayerEnabled := high(Integer);
+end;
+
+{ applies changes of settings record }
+procedure TScreenSing.ApplySettings;
+begin
+ //
+end;
+
+procedure TScreenSing.EndSong;
+begin
+ Settings.Finish := True;
+end;
+
+procedure TScreenSing.OnHide;
+begin
+ // background texture
+ if (Tex_Background.TexNum > 0) then
+ begin
+ glDeleteTextures(1, PGLuint(@Tex_Background.TexNum));
+ Tex_Background.TexNum := 0;
+ end;
+
+ Background.OnFinish;
+ Display.SetCursor;
+end;
+
+function TScreenSing.Draw: boolean;
+var
+ DisplayTime: real;
+ DisplayPrefix: string;
+ DisplayMin: integer;
+ DisplaySec: integer;
+ T: integer;
+ CurLyricsTime: real;
+ VideoFrameTime: Extended;
+ Line: TLyricLine;
+ LastWord: TLyricWord;
+begin
+ Background.Draw;
+
+ // draw background picture (if any, and if no visualizations)
+ // when we don't check for visualizations the visualizations would
+ // be overdrawn by the picture when {UNDEFINED UseTexture} in UVisualizer
+ if (not fShowVisualization) then
+ SingDrawBackground;
+
+ // set player names (for 2 screens and only singstar skin)
+ if ScreenAct = 1 then
+ begin
+ Text[TextP1].Text := 'P1';
+ Text[TextP1TwoP].Text := 'P1';
+ Text[TextP1ThreeP].Text := 'P1';
+ Text[TextP2R].Text := 'P2';
+ Text[TextP2M].Text := 'P2';
+ Text[TextP3R].Text := 'P3';
+ end;
+
+ if ScreenAct = 2 then
+ begin
+ case PlayersPlay of
+ 4:
+ begin
+ Text[TextP1TwoP].Text := 'P3';
+ Text[TextP2R].Text := 'P4';
+ end;
+ 6:
+ begin
+ Text[TextP1ThreeP].Text := 'P4';
+ Text[TextP2M].Text := 'P5';
+ Text[TextP3R].Text := 'P6';
+ end;
+ end; // case
+ end; // if
+
+ // retrieve current lyrics time, we have to store the value to avoid
+ // that min- and sec-values do not match
+ CurLyricsTime := LyricsState.GetCurrentTime();
+
+ // retrieve time for timebar text
+ case (fTimebarMode) of
+ tbmRemaining: begin
+ DisplayTime := LyricsState.TotalTime - CurLyricsTime;
+ DisplayPrefix := '-';
+ end;
+ tbmTotal: begin
+ DisplayTime := LyricsState.TotalTime;
+ DisplayPrefix := '#';
+ end;
+ else begin
+ DisplayTime := CurLyricsTime;
+ DisplayPrefix := '';
+ end;
+ end;
+ DisplayMin := Round(DisplayTime) div 60;
+ DisplaySec := Round(DisplayTime) mod 60;
+
+ // update static menu with time ...
+ Text[TextTimeText].Text := Format('%s%.2d:%.2d',
+ [DisplayPrefix, DisplayMin, DisplaySec]);
+
+ //the song was sung to the end?
+ Line := Lyrics.GetUpperLine();
+ if Line.LastLine then
+ begin
+ LastWord := Line.Words[Length(Line.Words)-1];
+ if CurLyricsTime >= GetTimeFromBeat(LastWord.Start+LastWord.Length) then
+ SungToEnd := true;
+ end;
+
+ // update and draw movie
+ if Assigned(fCurrentVideo) then
+ begin
+ // Just call this once
+ // when Screens = 2
+ if (ScreenAct = 1) then
+ begin
+ if (ShowFinish) then
+ begin
+ // everything is setup, determine the current position
+ VideoFrameTime := CurrentSong.VideoGAP + LyricsState.GetCurrentTime();
+ end
+ else
+ begin
+ // Important: do not yet start the triggered timer by a call to
+ // LyricsState.GetCurrentTime()
+ VideoFrameTime := CurrentSong.VideoGAP;
+ end;
+ fCurrentVideo.GetFrame(VideoFrameTime);
+ end;
+
+ fCurrentVideo.SetScreen(ScreenAct);
+ fCurrentVideo.Draw;
+ end;
+
+ // draw static menu (FG)
+ DrawFG;
+
+ // check for music finish
+ //Log.LogError('Check for music finish: ' + BoolToStr(Music.Finished) + ' ' + FloatToStr(LyricsState.CurrentTime*1000) + ' ' + IntToStr(CurrentSong.Finish));
+ if ShowFinish then
+ begin
+ if (not AudioPlayback.Finished) and
+ ((CurrentSong.Finish = 0) or
+ (LyricsState.GetCurrentTime() * 1000 <= CurrentSong.Finish)) and
+ (not Settings.Finish) then
+ begin
+ // analyze song if not paused
+ if (not Paused) then
+ begin
+ Sing(Self);
+ Party.CallOnSing;
+ end;
+ end
+ else
+ begin
+ if (not FadeOut) and (Screens=1) or (ScreenAct=2) then
+ begin
+ Finish;
+ FadeOut := true;
+ end;
+ end;
+ end;
+
+ // always draw custom items
+ SingDraw;
+
+ // goldennotestarstwinkle
+ GoldenRec.SpawnRec;
+
+ // draw scores
+ Scores.Draw;
+
+ // draw pausepopup
+ // FIXME: this is a workaround that the static is drawn over the lyrics, lines, scores and effects
+ // maybe someone could find a better solution
+ if Paused then
+ begin
+ Statics[StaticPausePopup].Visible := true;
+ Statics[StaticPausePopup].Draw;
+ Statics[StaticPausePopup].Visible := false;
+ end;
+
+ Result := true;
+end;
+
+procedure TScreenSing.Finish;
+begin
+ AudioInput.CaptureStop;
+ AudioPlayback.Stop;
+ AudioPlayback.SetSyncSource(nil);
+
+ LyricsState.Stop();
+ LyricsState.SetSyncSource(nil);
+
+ // close video files
+ fVideoClip := nil;
+ fCurrentVideo := nil;
+
+ // kill all stars and effects
+ GoldenRec.KillAll;
+
+ if (Ini.SavePlayback = 1) then
+ begin
+ Log.BenchmarkStart(0);
+ Log.LogVoice(0);
+ Log.LogVoice(1);
+ Log.LogVoice(2);
+ Log.BenchmarkEnd(0);
+ Log.LogBenchmark('Creating files', 0);
+ end;
+
+ SetFontItalic(false);
+
+ if not FadeOut then
+ Party.CallAfterSing;
+end;
+
+procedure TScreenSing.OnSentenceEnd(SentenceIndex: cardinal);
+var
+ PlayerIndex: byte;
+ CurrentPlayer: PPLayer;
+ CurrentScore: real;
+ Line: PLine;
+ LinePerfection: real; // perfection of singing performance on the current line
+ Rating: integer;
+ LineScore: real;
+ LineBonus: real;
+ MaxSongScore: integer; // max. points for the song (without line bonus)
+ MaxLineScore: real; // max. points for the current line
+const
+ // TODO: move this to a better place
+ MAX_LINE_RATING = 8; // max. rating for singing performance
+begin
+ Line := @Lines[0].Line[SentenceIndex];
+
+ // check for empty sentence
+ if (Line.TotalNotes <= 0) then
+ Exit;
+
+ // set max song score
+ if (Ini.LineBonus = 0) then
+ MaxSongScore := MAX_SONG_SCORE
+ else
+ MaxSongScore := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS;
+
+ // Note: ScoreValue is the sum of all note values of the song
+ MaxLineScore := MaxSongScore * (Line.TotalNotes / Lines[0].ScoreValue);
+
+ for PlayerIndex := 0 to High(Player) do
+ begin
+ CurrentPlayer := @Player[PlayerIndex];
+ CurrentScore := CurrentPlayer.Score + CurrentPlayer.ScoreGolden;
+
+ // line bonus
+
+ // points for this line
+ LineScore := CurrentScore - CurrentPlayer.ScoreLast;
+
+ // check for lines with low points
+ if (MaxLineScore <= 2) then
+ LinePerfection := 1
+ else
+ // determine LinePerfection
+ // Note: the "+2" extra points are a little bonus so the player does not
+ // have to be that perfect to reach the bonus steps.
+ LinePerfection := LineScore / (MaxLineScore - 2);
+
+ // clamp LinePerfection to range [0..1]
+ if (LinePerfection < 0) then
+ LinePerfection := 0
+ else if (LinePerfection > 1) then
+ LinePerfection := 1;
+
+ // add line-bonus if enabled
+ if (Ini.LineBonus > 0) then
+ begin
+ // line-bonus points (same for each line, no matter how long the line is)
+ LineBonus := MAX_SONG_LINE_BONUS / (Length(Lines[0].Line) -
+ NumEmptySentences);
+ // apply line-bonus
+ CurrentPlayer.ScoreLine :=
+ CurrentPlayer.ScoreLine + LineBonus * LinePerfection;
+ CurrentPlayer.ScoreLineInt := Floor(CurrentPlayer.ScoreLine / 10) * 10;
+ // update total score
+ CurrentPlayer.ScoreTotalInt :=
+ CurrentPlayer.ScoreInt +
+ CurrentPlayer.ScoreGoldenInt
+ + CurrentPlayer.ScoreLineInt;
+
+ // spawn rating pop-up
+ Rating := Round(LinePerfection * MAX_LINE_RATING);
+ Scores.SpawnPopUp(PlayerIndex, Rating, CurrentPlayer.ScoreTotalInt);
+ end
+ else
+ Scores.RaiseScore(PlayerIndex, CurrentPlayer.ScoreTotalInt);
+
+ // PerfectLineTwinkle (effect), part 1
+ if (Ini.EffectSing = 1) then
+ CurrentPlayer.LastSentencePerfect := (LinePerfection >= 1);
+
+ // refresh last score
+ CurrentPlayer.ScoreLast := CurrentScore;
+ end;
+
+ // PerfectLineTwinkle (effect), part 2
+ if (Ini.EffectSing = 1) then
+ GoldenRec.SpawnPerfectLineTwinkle;
+end;
+
+ // Called on sentence change
+ // SentenceIndex: index of the new active sentence
+procedure TScreenSing.OnSentenceChange(SentenceIndex: cardinal);
+begin
+ // goldenstarstwinkle
+ GoldenRec.SentenceChange;
+
+ // fill lyrics queue and set upper line to the current sentence
+ while (Lyrics.GetUpperLineIndex() < SentenceIndex) or
+ (not Lyrics.IsQueueFull) do
+ begin
+ // add the next line to the queue or a dummy if no more lines are available
+ if (Lyrics.LineCounter <= High(Lines[0].Line)) then
+ Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter])
+ else
+ Lyrics.AddLine(nil);
+ end;
+end;
+
+function TLyricsSyncSource.GetClock(): real;
+begin
+ Result := LyricsState.GetCurrentTime();
+end;
+
+function TMusicSyncSource.GetClock(): real;
+begin
+ Result := AudioPlayback.Position;
+end;
+
+end.
+
diff --git a/src/screens/UScreenSong.pas b/src/screens/UScreenSong.pas
new file mode 100644
index 00000000..6fe8d204
--- /dev/null
+++ b/src/screens/UScreenSong.pas
@@ -0,0 +1,2156 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenSong;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SysUtils,
+ SDL,
+ UCommon,
+ UDisplay,
+ UPath,
+ UFiles,
+ UIni,
+ ULanguage,
+ ULog,
+ UMenu,
+ UMenuEqualizer,
+ UMusic,
+ USong,
+ USongs,
+ UTexture,
+ UThemes,
+ UTime;
+
+type
+ TScreenSong = class(TMenu)
+ private
+ Equalizer: Tms_Equalizer;
+
+ PreviewOpened: Integer; // interaction of the Song that is loaded for preview music
+ // -1 if nothing is opened
+
+ isScrolling: boolean; // true if song flow is about to move
+
+ fCurrentVideo: IVideo;
+
+ procedure StartMusicPreview();
+ procedure StopMusicPreview();
+ procedure StartVideoPreview();
+ procedure StopVideoPreview();
+ public
+ TextArtist: integer;
+ TextTitle: integer;
+ TextNumber: integer;
+
+ //Video Icon Mod
+ VideoIcon: cardinal;
+
+ TextCat: integer;
+ StaticCat: integer;
+
+ SongCurrent: real;
+ SongTarget: real;
+
+ HighSpeed: boolean;
+ CoverFull: boolean;
+ CoverTime: real;
+
+ CoverX: integer;
+ CoverY: integer;
+ CoverW: integer;
+ is_jump: boolean; // Jump to Song Mod
+ is_jump_title:boolean; //Jump to SOng MOd-YTrue if search for Title
+
+ //Party Mod
+ Mode: TSingMode;
+
+ //party Statics (Joker)
+ StaticTeam1Joker1: cardinal;
+ StaticTeam1Joker2: cardinal;
+ StaticTeam1Joker3: cardinal;
+ StaticTeam1Joker4: cardinal;
+ StaticTeam1Joker5: cardinal;
+
+ StaticTeam2Joker1: cardinal;
+ StaticTeam2Joker2: cardinal;
+ StaticTeam2Joker3: cardinal;
+ StaticTeam2Joker4: cardinal;
+ StaticTeam2Joker5: cardinal;
+
+ StaticTeam3Joker1: cardinal;
+ StaticTeam3Joker2: cardinal;
+ StaticTeam3Joker3: cardinal;
+ StaticTeam3Joker4: cardinal;
+ StaticTeam3Joker5: cardinal;
+
+ StaticParty: array of cardinal;
+ TextParty: array of cardinal;
+ StaticNonParty: array of cardinal;
+ TextNonParty: array of cardinal;
+
+ constructor Create; override;
+ procedure SetScroll;
+ //procedure SetScroll1;
+ //procedure SetScroll2;
+ procedure SetScroll3;
+ procedure SetScroll4;
+ procedure SetScroll5;
+ procedure SetScroll6;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; override;
+ function Draw: boolean; override;
+ procedure GenerateThumbnails();
+ procedure OnShow; override;
+ procedure OnShowFinish; override;
+ procedure OnHide; override;
+ procedure SelectNext;
+ procedure SelectPrev;
+ procedure SkipTo(Target: cardinal);
+ procedure FixSelected; //Show Wrong Song when Tabs on Fix
+ procedure FixSelected2; //Show Wrong Song when Tabs on Fix
+ procedure ShowCatTL(Cat: integer);// Show Cat in Top left
+ procedure ShowCatTLCustom(Caption: UTF8String);// Show Custom Text in Top left
+ procedure HideCatTL;// Show Cat in Tob left
+ procedure Refresh; //Refresh Song Sorting
+ procedure ChangeMusic;
+ //Party Mode
+ procedure SelectRandomSong;
+ procedure SetJoker;
+ procedure SetStatics;
+ //procedures for Menu
+ procedure StartSong;
+ procedure OpenEditor;
+ procedure DoJoker(Team: integer);
+ procedure SelectPlayers;
+
+ procedure OnSongSelect; // called when song flows movement stops at a song
+ procedure OnSongDeSelect; // called before current song is deselected
+
+ procedure UnloadDetailedCover;
+
+ //Extensions
+ procedure DrawExtensions;
+ end;
+
+implementation
+
+uses
+ Math,
+ gl,
+ UCovers,
+ UGraphic,
+ UMain,
+ UMenuButton,
+ UNote,
+ UParty,
+ UPlaylist,
+ UScreenSongMenu,
+ USkins,
+ UUnicodeUtils;
+
+// ***** Public methods ****** //
+
+//Show Wrong Song when Tabs on Fix
+procedure TScreenSong.FixSelected;
+var
+ I, I2: integer;
+begin
+ if CatSongs.VisibleSongs > 0 then
+ begin
+ I2:= 0;
+ for I := Low(CatSongs.Song) to High(Catsongs.Song) do
+ begin
+ if CatSongs.Song[I].Visible then
+ inc(I2);
+
+ if I = Interaction - 1 then
+ break;
+ end;
+
+ SongCurrent := I2;
+ SongTarget := I2;
+ end;
+end;
+
+procedure TScreenSong.FixSelected2;
+var
+ I, I2: integer;
+begin
+ if CatSongs.VisibleSongs > 0 then
+ begin
+ I2:= 0;
+ for I := Low(CatSongs.Song) to High(Catsongs.Song) do
+ begin
+ if CatSongs.Song[I].Visible then
+ inc(I2);
+
+ if I = Interaction - 1 then
+ break;
+ end;
+
+ SongTarget := I2;
+ end;
+end;
+//Show Wrong Song when Tabs on Fix End
+
+procedure TScreenSong.ShowCatTLCustom(Caption: UTF8String);// Show Custom Text in Top left
+begin
+ Text[TextCat].Text := Caption;
+ Text[TextCat].Visible := true;
+ Statics[StaticCat].Visible := false;
+end;
+
+//Show Cat in Top Left Mod
+procedure TScreenSong.ShowCatTL(Cat: integer);
+begin
+ //Change
+ Text[TextCat].Text := CatSongs.Song[Cat].Artist;
+ //Statics[StaticCat].Texture := Texture.GetTexture(Button[Cat].Texture.Name, TEXTURE_TYPE_PLAIN, true);
+
+ //Show
+ Text[TextCat].Visible := true;
+ Statics[StaticCat].Visible := true;
+end;
+
+procedure TScreenSong.HideCatTL;
+begin
+ //Hide
+ //Text[TextCat].Visible := false;
+ Statics[StaticCat].Visible := false;
+ //New -> Show Text specified in Theme
+ Text[TextCat].Visible := true;
+ Text[TextCat].Text := Theme.Song.TextCat.Text;
+end;
+//Show Cat in Top Left Mod End
+
+// Method for input parsing. If false is returned, GetNextWindow
+// should be checked to know the next window to load;
+function TScreenSong.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+var
+ I: integer;
+ I2: integer;
+ SDL_ModState: word;
+ UpperLetter: UCS4Char;
+ TempStr: UTF8String;
+begin
+ Result := true;
+
+ //Song Screen Extensions (Jumpto + Menu)
+ if (ScreenSongMenu.Visible) then
+ begin
+ Result := ScreenSongMenu.ParseInput(PressedKey, CharCode, PressedDown);
+ Exit;
+ end
+ else if (ScreenSongJumpto.Visible) then
+ begin
+ Result := ScreenSongJumpto.ParseInput(PressedKey, CharCode, PressedDown);
+ Exit;
+ end;
+
+ if (PressedDown) then
+ begin // Key Down
+
+ SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT
+ + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT);
+
+ //Jump to Artist/Titel
+ if ((SDL_ModState and KMOD_LALT <> 0) and (Mode = smNormal)) then
+ begin
+ UpperLetter := UCS4UpperCase(CharCode);
+
+ if (UpperLetter in ([Ord('A')..Ord('Z'), Ord('0') .. Ord('9')]) ) then
+ begin
+ I2 := Length(CatSongs.Song);
+
+ //Jump To Titel
+ if (SDL_ModState = (KMOD_LALT or KMOD_LSHIFT)) then
+ begin
+ for I := 1 to High(CatSongs.Song) do
+ begin
+ if (CatSongs.Song[(I + Interaction) mod I2].Visible) then
+ begin
+ TempStr := CatSongs.Song[(I + Interaction) mod I2].Title;
+ if (Length(TempStr) > 0) and
+ (UCS4UpperCase(UTF8ToUCS4String(TempStr)[0]) = UpperLetter) then
+ begin
+ SkipTo(CatSongs.VisibleIndex((I + Interaction) mod I2));
+
+ AudioPlayback.PlaySound(SoundLib.Change);
+
+ SetScroll4;
+ //Break and Exit
+ Exit;
+ end;
+ end;
+ end;
+ end
+ //Jump to Artist
+ else if (SDL_ModState = KMOD_LALT) then
+ begin
+ for I := 1 to High(CatSongs.Song) do
+ begin
+ if (CatSongs.Song[(I + Interaction) mod I2].Visible) then
+ begin
+ TempStr := CatSongs.Song[(I + Interaction) mod I2].Artist;
+ if (Length(TempStr) > 0) and
+ (UCS4UpperCase(UTF8ToUCS4String(TempStr)[0]) = UpperLetter) then
+ begin
+ SkipTo(CatSongs.VisibleIndex((I + Interaction) mod I2));
+
+ AudioPlayback.PlaySound(SoundLib.Change);
+
+ SetScroll4;
+
+ //Break and Exit
+ Exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ Exit;
+ end;
+
+ // **********************
+ // * workaround for LCTRL+R: it should be changed when we have a solution for the
+ // * CTRL+'A'..'Z' problem
+ if (SDL_ModState = KMOD_LCTRL) and (PressedKey = SDLK_R) then
+ CharCode := UCS4Char('R');
+ // **********************
+
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+
+ Ord('M'): //Show SongMenu
+ begin
+ if (Songs.SongList.Count > 0) then
+ begin
+ if (Mode = smNormal) then
+ begin
+ if (not CatSongs.Song[Interaction].Main) then // clicked on Song
+ begin
+ if CatSongs.CatNumShow = -3 then
+ begin
+ ScreenSongMenu.OnShow;
+ ScreenSongMenu.MenuShow(SM_Playlist);
+ end
+ else
+ begin
+ ScreenSongMenu.OnShow;
+ ScreenSongMenu.MenuShow(SM_Main);
+ end;
+ end
+ else
+ begin
+ ScreenSongMenu.OnShow;
+ ScreenSongMenu.MenuShow(SM_Playlist_Load);
+ end;
+ end //Party Mode -> Show Party Menu
+ else
+ begin
+ ScreenSongMenu.OnShow;
+ ScreenSongMenu.MenuShow(SM_Party_Main);
+ end;
+ end;
+ Exit;
+ end;
+
+ Ord('P'): //Show Playlist Menu
+ begin
+ if (Songs.SongList.Count > 0) and (Mode = smNormal) then
+ begin
+ ScreenSongMenu.OnShow;
+ ScreenSongMenu.MenuShow(SM_Playlist_Load);
+ end;
+ Exit;
+ end;
+
+ Ord('J'): //Show Jumpto Menu
+ begin
+ if (Songs.SongList.Count > 0) and (Mode = smNormal) then
+ begin
+ ScreenSongJumpto.Visible := true;
+ end;
+ Exit;
+ end;
+
+ Ord('E'):
+ begin
+ OpenEditor;
+ Exit;
+ end;
+
+ Ord('R'):
+ begin
+ if (Songs.SongList.Count > 0) and
+ (Mode = smNormal) then
+ begin
+ if (SDL_ModState = KMOD_LSHIFT) and (Ini.TabsAtStartup = 1) then // random category
+ begin
+ I2 := 0; // count cats
+ for I := 0 to High(CatSongs.Song) do
+ begin
+ if CatSongs.Song[I].Main then
+ Inc(I2);
+ end;
+
+ I2 := Random(I2 + 1); // random and include I2
+
+ // find cat:
+ for I := 0 to High(CatSongs.Song) do
+ begin
+ if CatSongs.Song[I].Main then
+ Dec(I2);
+ if (I2 <= 0) then
+ begin
+ // show cat in top left mod
+ ShowCatTL (I);
+
+ Interaction := I;
+
+ CatSongs.ShowCategoryList;
+ CatSongs.ClickCategoryButton(I);
+ SelectNext;
+ FixSelected;
+ break;
+ end;
+ end;
+ end
+ else if (SDL_ModState = KMOD_LCTRL) and (Ini.TabsAtStartup = 1) then // random in all categories
+ begin
+ repeat
+ I2 := Random(High(CatSongs.Song) + 1);
+ until (not CatSongs.Song[I2].Main);
+
+ // search cat
+ for I := I2 downto 0 do
+ begin
+ if CatSongs.Song[I].Main then
+ break;
+ end;
+
+ // in I is now the categorie in I2 the song
+
+ // choose cat
+ CatSongs.ShowCategoryList;
+
+ // show cat in top left mod
+ ShowCatTL (I);
+
+ CatSongs.ClickCategoryButton(I);
+ SelectNext;
+
+ // Fix: not existing song selected:
+ //if (I + 1 = I2) then
+ Inc(I2);
+
+ // choose song
+ SkipTo(I2 - I);
+ end
+ else // random in one category
+ begin
+ SkipTo(Random(CatSongs.VisibleSongs));
+ end;
+ AudioPlayback.PlaySound(SoundLib.Change);
+
+ SetScroll4;
+ end;
+ Exit;
+ end;
+ end; // normal keys
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ if (Mode = smNormal) then
+ begin
+ //On Escape goto Cat-List Hack
+ if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow <> -1) then
+ begin
+ //Find Category
+ I := Interaction;
+ while (not CatSongs.Song[I].Main) do
+ begin
+ Dec(I);
+ if (I < 0) then
+ break;
+ end;
+ if (I <= 1) then
+ Interaction := High(CatSongs.Song)
+ else
+ Interaction := I - 1;
+
+ //Stop Music
+ StopMusicPreview();
+
+ CatSongs.ShowCategoryList;
+
+ //Show Cat in Top Left Mod
+ HideCatTL;
+
+ //Show Wrong Song when Tabs on Fix
+ SelectNext;
+ FixSelected;
+ //SelectPrev(true);
+ //CatSongs.Song[0].Visible := false;
+ end
+ else
+ begin
+ //On Escape goto Cat-List Hack End
+ //Tabs off and in Search or Playlist -> Go back to Song view
+ if (CatSongs.CatNumShow < -1) then
+ begin
+ //Atm: Set Empty Filter
+ CatSongs.SetFilter('', fltAll);
+
+ //Show Cat in Top Left Mod
+ HideCatTL;
+ Interaction := 0;
+
+ //Show Wrong Song when Tabs on Fix
+ SelectNext;
+ FixSelected;
+ end
+ else
+ begin
+ StopMusicPreview();
+ AudioPlayback.PlaySound(SoundLib.Back);
+
+ FadeTo(@ScreenMain);
+ end;
+
+ end;
+ end
+ //When in party Mode then Ask before Close
+ else if (Mode = smPartyMode) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ CheckFadeTo(@ScreenMain,'MSG_END_PARTY');
+ end;
+ end;
+ SDLK_RETURN:
+ begin
+ if (Songs.SongList.Count > 0) then
+ begin
+ if CatSongs.Song[Interaction].Main then
+ begin // clicked on Category Button
+ //Show Cat in Top Left Mod
+ ShowCatTL (Interaction);
+
+ //I := CatSongs.VisibleIndex(Interaction);
+ CatSongs.ClickCategoryButton(Interaction);
+ {I2 := CatSongs.VisibleIndex(Interaction);
+ SongCurrent := SongCurrent - I + I2;
+ SongTarget := SongTarget - I + I2; }
+
+ // SetScroll4;
+
+ //Show Wrong Song when Tabs on Fix
+ SelectNext;
+ FixSelected;
+ end
+ else
+ begin // clicked on song
+ if (Mode = smNormal) then //Normal Mode -> Start Song
+ begin
+ //Do the Action that is specified in Ini
+ case Ini.OnSongClick of
+ 0: StartSong;
+ 1: SelectPlayers;
+ 2:begin
+ if (CatSongs.CatNumShow = -3) then
+ ScreenSongMenu.MenuShow(SM_Playlist)
+ else
+ ScreenSongMenu.MenuShow(SM_Main);
+ end;
+ end;
+ end
+ else if (Mode = smPartyMode) then //PartyMode -> Show Menu
+ begin
+ if (Ini.PartyPopup = 1) then
+ ScreenSongMenu.MenuShow(SM_Party_Main)
+ else
+ Party.CallAfterSongSelect;
+ end;
+ end;
+ end;
+ end;
+
+ SDLK_DOWN:
+ begin
+ if (Mode = smNormal) then
+ begin
+ //Only Change Cat when not in Playlist or Search Mode
+ if (CatSongs.CatNumShow > -2) then
+ begin
+ //Cat Change Hack
+ if Ini.TabsAtStartup = 1 then
+ begin
+ I := Interaction;
+ if I <= 0 then
+ I := 1;
+
+ while not catsongs.Song[I].Main do
+ begin
+ Inc (I);
+ if (I > High(catsongs.Song)) then
+ I := Low(catsongs.Song);
+ end;
+
+ Interaction := I;
+
+ //Show Cat in Top Left Mod
+ ShowCatTL (Interaction);
+
+ CatSongs.ClickCategoryButton(Interaction);
+ SelectNext;
+ FixSelected;
+
+ //Play Music:
+ AudioPlayback.PlaySound(SoundLib.Change);
+ end;
+
+ //
+ //Cat Change Hack End}
+ end;
+ end;
+ end;
+ SDLK_UP:
+ begin
+ if (Mode = smNormal) then
+ begin
+ //Only Change Cat when not in Playlist or Search Mode
+ if (CatSongs.CatNumShow > -2) then
+ begin
+ //Cat Change Hack
+ if Ini.TabsAtStartup = 1 then
+ begin
+ I := Interaction;
+ I2 := 0;
+ if I <= 0 then
+ I := 1;
+
+ while not catsongs.Song[I].Main or (I2 = 0) do
+ begin
+ if catsongs.Song[I].Main then
+ Inc(I2);
+ Dec (I);
+ if (I < Low(catsongs.Song)) then
+ I := High(catsongs.Song);
+ end;
+
+ Interaction := I;
+
+ //Show Cat in Top Left Mod
+ ShowCatTL (I);
+
+ CatSongs.ClickCategoryButton(I);
+ SelectNext;
+ FixSelected;
+
+ //Play Music:
+ AudioPlayback.PlaySound(SoundLib.Change);
+ end;
+ end;
+ //Cat Change Hack End}
+ end;
+ end;
+
+ SDLK_RIGHT:
+ begin
+ if (Songs.SongList.Count > 0) and (Mode = smNormal) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Change);
+ SelectNext;
+ SetScroll4;
+ end;
+ end;
+
+ SDLK_LEFT:
+ begin
+ if (Songs.SongList.Count > 0)and (Mode = smNormal) then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Change);
+ SelectPrev;
+ SetScroll4;
+ end;
+ end;
+
+ SDLK_1:
+ begin //Joker
+ DoJoker(0);
+ end;
+
+ SDLK_2:
+ begin //Joker
+ DoJoker(1);
+ end;
+
+ SDLK_3:
+ begin //Joker
+ DoJoker(2);
+ end;
+ end;
+ end; // if (PressedDown)
+end;
+
+function TScreenSong.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean;
+ var
+ I, J: Integer;
+ Btn: Integer;
+begin
+ Result := true;
+
+ if (ScreenSongMenu.Visible) then
+ begin
+ Result := ScreenSongMenu.ParseMouse(MouseButton, BtnDown, X, Y);
+ exit;
+ end
+ else if (ScreenSongJumpTo.Visible) then
+ begin
+ Result := ScreenSongJumpTo.ParseMouse(MouseButton, BtnDown, X, Y);
+ exit;
+ end
+ else // no extension visible
+ begin
+ if (BtnDown) then
+ begin
+ //if RightMbESC is set, send ESC keypress
+ if RightMbESC and (MouseButton = SDL_BUTTON_RIGHT) then
+ Result:=ParseInput(SDLK_ESCAPE, 0, true)
+
+ //song scrolling with mousewheel
+ else if (MouseButton = SDL_BUTTON_WHEELDOWN) then
+ ParseInput(SDLK_RIGHT, 0, true)
+
+ else if (MouseButton = SDL_BUTTON_WHEELUP) then
+ ParseInput(SDLK_LEFT, 0, true)
+
+ //LMB anywhere starts
+ else if (MouseButton = SDL_BUTTON_LEFT) then
+ begin
+ if (CatSongs.VisibleSongs > 4) then
+ begin
+ // select the second visible button left from selected
+ I := 0;
+ Btn := Interaction;
+ while (I < 2) do
+ begin
+ Dec(Btn);
+ if (Btn < 0) then
+ Btn := High(CatSongs.Song);
+
+ if (CatSongs.Song[Btn].Visible) then
+ Inc(I);
+ end;
+
+ // test the 5 front buttons for click
+ for I := 0 to 4 do
+ begin
+ if InRegion(X, Y, Button[Btn].GetMouseOverArea) then
+ begin
+ // song cover clicked
+ if (I = 2) then
+ begin // Selected Song clicked -> start singing
+ ParseInput(SDLK_RETURN, 0, true);
+ end
+ else
+ begin // one of the other 4 covers in the front clicked -> select it
+ J := I - 2;
+ while (J < 0) do
+ begin
+ ParseInput(SDLK_LEFT, 0, true);
+ Inc(J);
+ end;
+
+ while (J > 0) do
+ begin
+ ParseInput(SDLK_RIGHT, 0, true);
+ Dec(J);
+ end;
+ end;
+ Break;
+ end;
+
+ Btn := CatSongs.FindNextVisible(Btn);
+ if (Btn = -1) then
+ Break;
+ end;
+ end
+ else
+ ParseInput(SDLK_RETURN, 0, true);
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenSong.Create;
+var
+ i: integer;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.Song);
+
+ TextArtist := AddText(Theme.Song.TextArtist);
+ TextTitle := AddText(Theme.Song.TextTitle);
+ TextNumber := AddText(Theme.Song.TextNumber);
+
+ //Show Cat in Top Left mod
+ TextCat := AddText(Theme.Song.TextCat);
+ StaticCat := AddStatic(Theme.Song.StaticCat);
+
+ //Show Video Icon Mod
+ VideoIcon := AddStatic(Theme.Song.VideoIcon);
+
+ //Party Mode
+ StaticTeam1Joker1 := AddStatic(Theme.Song.StaticTeam1Joker1);
+ StaticTeam1Joker2 := AddStatic(Theme.Song.StaticTeam1Joker2);
+ StaticTeam1Joker3 := AddStatic(Theme.Song.StaticTeam1Joker3);
+ StaticTeam1Joker4 := AddStatic(Theme.Song.StaticTeam1Joker4);
+ StaticTeam1Joker5 := AddStatic(Theme.Song.StaticTeam1Joker5);
+
+ StaticTeam2Joker1 := AddStatic(Theme.Song.StaticTeam2Joker1);
+ StaticTeam2Joker2 := AddStatic(Theme.Song.StaticTeam2Joker2);
+ StaticTeam2Joker3 := AddStatic(Theme.Song.StaticTeam2Joker3);
+ StaticTeam2Joker4 := AddStatic(Theme.Song.StaticTeam2Joker4);
+ StaticTeam2Joker5 := AddStatic(Theme.Song.StaticTeam2Joker5);
+
+ StaticTeam3Joker1 := AddStatic(Theme.Song.StaticTeam3Joker1);
+ StaticTeam3Joker2 := AddStatic(Theme.Song.StaticTeam3Joker2);
+ StaticTeam3Joker3 := AddStatic(Theme.Song.StaticTeam3Joker3);
+ StaticTeam3Joker4 := AddStatic(Theme.Song.StaticTeam3Joker4);
+ StaticTeam3Joker5 := AddStatic(Theme.Song.StaticTeam3Joker5);
+
+ //Load Party or NonParty specific Statics and Texts
+ SetLength(StaticParty, Length(Theme.Song.StaticParty));
+ for i := 0 to High(Theme.Song.StaticParty) do
+ StaticParty[i] := AddStatic(Theme.Song.StaticParty[i]);
+
+ SetLength(TextParty, Length(Theme.Song.TextParty));
+ for i := 0 to High(Theme.Song.TextParty) do
+ TextParty[i] := AddText(Theme.Song.TextParty[i]);
+
+ SetLength(StaticNonParty, Length(Theme.Song.StaticNonParty));
+ for i := 0 to High(Theme.Song.StaticNonParty) do
+ StaticNonParty[i] := AddStatic(Theme.Song.StaticNonParty[i]);
+
+ SetLength(TextNonParty, Length(Theme.Song.TextNonParty));
+ for i := 0 to High(Theme.Song.TextNonParty) do
+ TextNonParty[i] := AddText(Theme.Song.TextNonParty[i]);
+
+ // Song List
+ //Songs.LoadSongList; // moved to the UltraStar unit
+ CatSongs.Refresh;
+
+ GenerateThumbnails();
+
+ // Randomize Patch
+ Randomize;
+
+ Equalizer := Tms_Equalizer.Create(AudioPlayback, Theme.Song.Equalizer);
+
+ PreviewOpened := -1;
+ isScrolling := false;
+
+ fCurrentVideo := nil;
+end;
+
+procedure TScreenSong.GenerateThumbnails();
+var
+ I: integer;
+ CoverButtonIndex: integer;
+ CoverButton: TButton;
+ CoverTexture: TTexture;
+ Cover: TCover;
+ CoverFile: IPath;
+ Song: TSong;
+begin
+ if (Length(CatSongs.Song) <= 0) then
+ Exit;
+
+ // set length of button array once instead for every song
+ SetButtonLength(Length(CatSongs.Song));
+
+ // create all buttons
+ for I := 0 to High(CatSongs.Song) do
+ begin
+ CoverButton := nil;
+
+ // create a clickable cover
+ CoverButtonIndex := AddButton(300 + I*250, 140, 200, 200, PATH_NONE, TEXTURE_TYPE_PLAIN, Theme.Song.Cover.Reflections);
+ if (CoverButtonIndex > -1) then
+ CoverButton := Button[CoverButtonIndex];
+ if (CoverButton = nil) then
+ Continue;
+
+ Song := CatSongs.Song[I];
+
+ CoverFile := Song.Path.Append(Song.Cover);
+ if (not CoverFile.IsFile()) then
+ Song.Cover := PATH_NONE;
+
+ if (Song.Cover.IsUnset) then
+ CoverFile := Skin.GetTextureFileName('SongCover');
+
+ // load cover and cache its texture
+ Cover := Covers.FindCover(CoverFile);
+ if (Cover = nil) then
+ Cover := Covers.AddCover(CoverFile);
+
+ // use the cached texture
+ // TODO: this is a workaround until the new song-loading works.
+ // The TCover object should be added to the song-object. The thumbnails
+ // should be loaded each time the song-screen is shown (it is real fast).
+ // This way, we will not waste that much memory and have a link between
+ // song and cover.
+ if (Cover <> nil) then
+ begin
+ CoverTexture := Cover.GetPreviewTexture();
+ Texture.AddTexture(CoverTexture, TEXTURE_TYPE_PLAIN, true);
+ CoverButton.Texture := CoverTexture;
+
+ // set selected to false -> the right texture will be displayed
+ CoverButton.Selected := False;
+ end;
+
+ Cover.Free;
+ end;
+
+ // reset selection
+ if (Length(CatSongs.Song) > 0) then
+ Interaction := 0;
+end;
+
+{ called when song flows movement stops at a song }
+procedure TScreenSong.OnSongSelect;
+begin
+ if (Ini.PreviewVolume <> 0) then
+ begin
+ StartMusicPreview;
+ StartVideoPreview;
+ end;
+
+ // fade in detailed cover
+ CoverTime := 0;
+end;
+
+{ called before current song is deselected }
+procedure TScreenSong.OnSongDeSelect;
+begin
+ CoverTime := 10;
+ UnLoadDetailedCover;
+
+ StopMusicPreview();
+ StopVideoPreview();
+ PreviewOpened := -1;
+end;
+
+procedure TScreenSong.SetScroll;
+var
+ VS, B: integer;
+begin
+ VS := CatSongs.VisibleSongs;
+ if VS > 0 then
+ begin
+ // Set Positions
+ case Theme.Song.Cover.Style of
+ 3: SetScroll3;
+ 5: SetScroll5;
+ 6: SetScroll6;
+ else SetScroll4;
+ end;
+
+ // Set visibility of video icon
+ Statics[VideoIcon].Visible := CatSongs.Song[Interaction].Video.IsSet;
+
+ // Set texts
+ Text[TextArtist].Text := CatSongs.Song[Interaction].Artist;
+ Text[TextTitle].Text := CatSongs.Song[Interaction].Title;
+ if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow = -1) then
+ begin
+ Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].OrderNum) + '/' + IntToStr(CatSongs.CatCount);
+ Text[TextTitle].Text := '(' + IntToStr(CatSongs.Song[Interaction].CatNumber) + ' ' + Language.Translate('SING_SONGS_IN_CAT') + ')';
+ end
+ else if (CatSongs.CatNumShow = -2) then
+ Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS)
+ else if (CatSongs.CatNumShow = -3) then
+ Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS)
+ else if (Ini.TabsAtStartup = 1) then
+ Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].CatNumber) + '/' + IntToStr(CatSongs.Song[Interaction - CatSongs.Song[Interaction].CatNumber].CatNumber)
+ else
+ Text[TextNumber].Text := IntToStr(Interaction+1) + '/' + IntToStr(Length(CatSongs.Song));
+ end
+ else
+ begin
+ Text[TextNumber].Text := '0/0';
+ Text[TextArtist].Text := '';
+ Text[TextTitle].Text := '';
+ for B := 0 to High(Button) do
+ Button[B].Visible := false;
+
+ end;
+end;
+
+(*
+procedure TScreenSong.SetScroll1;
+var
+ B: integer; // button
+ Src: integer;
+ //Dst: integer;
+ Count: integer; // Dst is not used. Count is used.
+ Ready: boolean;
+
+ VisCount: integer; // count of visible (or selectable) buttons
+ VisInt: integer; // visible position of interacted button
+ Typ: integer; // 0 when all songs fits the screen
+ Placed: integer; // number of placed visible buttons
+begin
+ //Src := 0;
+ //Dst := -1;
+ Count := 1;
+ Typ := 0;
+ Ready := false;
+ Placed := 0;
+
+ VisCount := 0;
+ for B := 0 to High(Button) do
+ if CatSongs.Song[B].Visible then
+ Inc(VisCount);
+
+ VisInt := 0;
+ for B := 0 to Interaction-1 do
+ if CatSongs.Song[B].Visible then
+ Inc(VisInt);
+
+ if VisCount <= 6 then
+ begin
+ Typ := 0;
+ end
+ else
+ begin
+ if VisInt <= 3 then
+ begin
+ Typ := 1;
+ Count := 7;
+ Ready := true;
+ end;
+
+ if (VisCount - VisInt) <= 3 then
+ begin
+ Typ := 2;
+ Count := 7;
+ Ready := true;
+ end;
+
+ if not Ready then
+ begin
+ Typ := 3;
+ Src := Interaction;
+ end;
+ end;
+
+
+ // hide all buttons
+ for B := 0 to High(Button) do
+ begin
+ Button[B].Visible := false;
+ Button[B].Selectable := CatSongs.Song[B].Visible;
+ end;
+
+ {
+ for B := Src to Dst do
+ begin
+ //Button[B].Visible := true;
+ Button[B].Visible := CatSongs.Song[B].Visible;
+ Button[B].Selectable := Button[B].Visible;
+ Button[B].Y := 140 + (B-Src) * 60;
+ end;
+ }
+
+ if Typ = 0 then
+ begin
+ for B := 0 to High(Button) do
+ begin
+ if CatSongs.Song[B].Visible then
+ begin
+ Button[B].Visible := true;
+ Button[B].Y := 140 + (Placed) * 60;
+ Inc(Placed);
+ end;
+ end;
+ end;
+
+ if Typ = 1 then
+ begin
+ B := 0;
+ while (Count > 0) do
+ begin
+ if CatSongs.Song[B].Visible then
+ begin
+ Button[B].Visible := true;
+ Button[B].Y := 140 + (Placed) * 60;
+ Inc(Placed);
+ Dec(Count);
+ end;
+ Inc(B);
+ end;
+ end;
+
+ if Typ = 2 then
+ begin
+ B := High(Button);
+ while (Count > 0) do
+ begin
+ if CatSongs.Song[B].Visible then
+ begin
+ Button[B].Visible := true;
+ Button[B].Y := 140 + (6-Placed) * 60;
+ Inc(Placed);
+ Dec(Count);
+ end;
+ Dec(B);
+ end;
+ end;
+
+ if Typ = 3 then
+ begin
+ B := Src;
+ Count := 4;
+ while (Count > 0) do
+ begin
+ if CatSongs.Song[B].Visible then
+ begin
+ Button[B].Visible := true;
+ Button[B].Y := 140 + (3+Placed) * 60;
+ Inc(Placed);
+ Dec(Count);
+ end;
+ Inc(B);
+ end;
+
+ B := Src-1;
+ Placed := 0;
+ Count := 3;
+ while (Count > 0) do
+ begin
+ if CatSongs.Song[B].Visible then
+ begin
+ Button[B].Visible := true;
+ Button[B].Y := 140 + (2-Placed) * 60;
+ Inc(Placed);
+ Dec(Count);
+ end;
+ Dec(B);
+ end;
+
+ end;
+
+ if Length(Button) > 0 then
+ Statics[1].Texture.Y := Button[Interaction].Y - 5; // selection texture
+end;
+
+procedure TScreenSong.SetScroll2;
+var
+ B: integer;
+ //Factor: integer; // factor of position relative to center of screen
+ //Factor2: real;
+begin
+ // line
+ for B := 0 to High(Button) do
+ Button[B].X := 300 + (B - Interaction) * 260;
+
+ if Length(Button) >= 3 then
+ begin
+ if Interaction = 0 then
+ Button[High(Button)].X := 300 - 260;
+
+ if Interaction = High(Button) then
+ Button[0].X := 300 + 260;
+ end;
+
+ // circle
+ {
+ for B := 0 to High(Button) do
+ begin
+ Factor := (B - Interaction); // 0 to center, -1: to left, +1 to right
+ Factor2 := Factor / Length(Button);
+ Button[B].X := 300 + 10000 * sin(2*pi*Factor2);
+ //Button[B].Y := 140 + 50 * ;
+ end;
+ }
+end;
+*)
+
+procedure TScreenSong.SetScroll3; // with slide
+var
+ B: integer;
+ //Factor: integer; // factor of position relative to center of screen
+ //Factor2: real;
+begin
+ SongTarget := Interaction;
+
+ // line
+ for B := 0 to High(Button) do
+ begin
+ Button[B].X := 300 + (B - SongCurrent) * 260;
+ if (Button[B].X < -Button[B].W) or (Button[B].X > 800) then
+ Button[B].Visible := false
+ else
+ Button[B].Visible := true;
+ end;
+
+ {
+ if Length(Button) >= 3 then
+ begin
+ if Interaction = 0 then
+ Button[High(Button)].X := 300 - 260;
+
+ if Interaction = High(Button) then
+ Button[0].X := 300 + 260;
+ end;
+ }
+
+ // circle
+ {
+ for B := 0 to High(Button) do
+ begin
+ Factor := (B - Interaction); // 0 to center, -1: to left, +1 to right
+ Factor2 := Factor / Length(Button);
+ Button[B].X := 300 + 10000 * sin(2*pi*Factor2);
+ //Button[B].Y := 140 + 50 * ;
+ end;
+ }
+end;
+
+(**
+ * Rotation
+ *)
+procedure TScreenSong.SetScroll4;
+var
+ B: integer;
+ Angle: real;
+ Z, Z2: real;
+ VS: integer;
+begin
+ VS := CatSongs.VisibleSongs();
+
+ for B := 0 to High(Button) do
+ begin
+ Button[B].Visible := CatSongs.Song[B].Visible;
+ if Button[B].Visible then
+ begin
+ // angle between the cover and selected song-cover in radians
+ Angle := 2*Pi * (CatSongs.VisibleIndex(B) - SongCurrent) / VS;
+
+ // calc z-position from angle
+ Z := (1 + cos(Angle)) / 2; // scaled to range [0..1]
+ Z2 := (1 + 2*Z) / 3; // scaled to range [1/3..1]
+
+ // adjust cover's width and height according its z-position
+ // Note: Theme.Song.Cover.W is not used as width and height are equal
+ // and Theme.Song.Cover.W is used as circle radius in Scroll5.
+ Button[B].W := Theme.Song.Cover.H * Z2;
+ Button[B].H := Button[B].W;
+
+ // set cover position
+ Button[B].X := Theme.Song.Cover.X +
+ (0.185 * Theme.Song.Cover.H * VS * sin(Angle)) * Z2 -
+ ((Button[B].H - Theme.Song.Cover.H)/2);
+ Button[B].Y := Theme.Song.Cover.Y +
+ (Theme.Song.Cover.H - Abs(Button[B].H)) * 0.7;
+ Button[B].Z := Z / 2 + 0.3;
+ end;
+ end;
+end;
+
+(**
+ * rotate
+ *)
+procedure TScreenSong.SetScroll5;
+var
+ B: integer;
+ Angle: real;
+ Pos: real;
+ VS: integer;
+ Padding: real;
+ X: real;
+ {
+ Theme.Song.CoverW: circle radius
+ Theme.Song.CoverX: x-pos. of the left edge of the selected cover
+ Theme.Song.CoverY: y-pos. of the upper edge of the selected cover
+ Theme.Song.CoverH: cover height
+ }
+begin
+ VS := CatSongs.VisibleSongs();
+
+ // Update positions of all buttons
+ for B := 0 to High(Button) do
+ begin
+ Button[B].Visible := CatSongs.Song[B].Visible; // adjust visibility
+ if Button[B].Visible then // Only change pos for visible buttons
+ begin
+ // Pos is the distance to the centered cover in the range [-VS/2..+VS/2]
+ Pos := (CatSongs.VisibleIndex(B) - SongCurrent);
+ if (Pos < -VS/2) then
+ Pos := Pos + VS
+ else if (Pos > VS/2) then
+ Pos := Pos - VS;
+
+ // Avoid overlapping of the front covers.
+ // Use an alternate position for the five front covers.
+ if (Abs(Pos) < 2.5) then
+ begin
+ Angle := Pi * (Pos / Min(VS, 5)); // Range: (-1/4*Pi .. +1/4*Pi)
+
+ Button[B].H := Abs(Theme.Song.Cover.H * cos(Angle*0.8));
+ Button[B].W := Button[B].H;
+
+ //Button[B].Reflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;
+ Button[B].DeSelectReflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;
+
+ Padding := (Button[B].H - Theme.Song.Cover.H)/2;
+ X := Sin(Angle*1.3) * 0.9;
+
+ Button[B].X := Theme.Song.Cover.X + Theme.Song.Cover.W * X - Padding;
+ Button[B].Y := (Theme.Song.Cover.Y + (Theme.Song.Cover.H - Abs(Theme.Song.Cover.H * cos(Angle))) * 0.5);
+ Button[B].Z := 0.95 - Abs(Pos) * 0.01;
+
+ if VS < 5 then
+ Button[B].Texture.Alpha := 1 - Abs(Pos) / VS * 2
+ else
+ Button[B].Texture.Alpha := 1;
+ end
+ { only draw 3 visible covers in the background
+ (the 3 that are on the opposite of the front covers}
+ else if (VS > 7) and (Abs(Pos) > floor(VS/2) - 1.5) then
+ begin
+ // Transform Pos to range [-1..-3/4, +3/4..+1]
+ { the 3 covers at the back will show up in the gap between the
+ front cover and its neighbors
+ one cover will be hiddenbehind the front cover,
+ but this will not be a lack of performance ;) }
+ if Pos < 0 then
+ Pos := (Pos - 2 + ceil(VS/2))/8 - 0.75
+ else
+ Pos := (Pos + 2 - floor(VS/2))/8 + 0.75;
+
+ // angle in radians [-2Pi..-Pi, +Pi..+2Pi]
+ Angle := 2*Pi * Pos;
+
+ Button[B].H := 0.6*(Theme.Song.Cover.H-Abs(Theme.Song.Cover.H * cos(Angle/2)*0.8));
+ Button[B].W := Button[B].H;
+
+ Padding := (Button[B].H - Theme.Song.Cover.H)/2;
+
+ Button[B].X := Theme.Song.Cover.X+Theme.Song.Cover.H/2-Button[b].H/2+Theme.Song.Cover.W/320*((Theme.Song.Cover.H)*sin(Angle/2)*1.52);
+ Button[B].Y := Theme.Song.Cover.Y - (Button[B].H - Theme.Song.Cover.H)*0.75;
+ Button[B].Z := (0.4 - Abs(Pos/4)) -0.00001; //z < 0.49999 is behind the cover 1 is in front of the covers
+
+ Button[B].Texture.Alpha := 1;
+
+ //Button[B].Reflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;
+ Button[B].DeSelectReflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;
+ end
+ { all other covers are not visible }
+ else
+ Button[B].Visible := false;
+ end;
+ end;
+end;
+
+procedure TScreenSong.SetScroll6; // rotate (slotmachine style)
+var
+ B: integer;
+ Angle: real;
+ Pos: real;
+ VS: integer;
+ diff: real;
+ X: real;
+ Factor: real;
+ Z, Z2: real;
+begin
+ VS := CatSongs.VisibleSongs;
+ if VS <= 5 then
+ begin
+ // circle
+ for B := 0 to High(Button) do
+ begin
+ Button[B].Visible := CatSongs.Song[B].Visible;
+ if Button[B].Visible then // optimization for 1000 songs - updates only visible songs, hiding in tabs becomes useful for maintaing good speed
+ begin
+
+ Factor := 2 * pi * (CatSongs.VisibleIndex(B) - SongCurrent) / VS {CatSongs.VisibleSongs};// 0.5.0 (II): takes another 16ms
+
+ Z := (1 + cos(Factor)) / 2;
+ Z2 := (1 + 2*Z) / 3;
+
+ Button[B].Y := Theme.Song.Cover.Y + (0.185 * Theme.Song.Cover.H * VS * sin(Factor)) * Z2 - ((Button[B].H - Theme.Song.Cover.H)/2); // 0.5.0 (I): 2 times faster by not calling CatSongs.VisibleSongs
+ Button[B].Z := Z / 2 + 0.3;
+
+ Button[B].W := Theme.Song.Cover.H * Z2;
+
+ //Button[B].Y := {50 +} 140 + 50 - 50 * Z2;
+ Button[B].X := Theme.Song.Cover.X + (Theme.Song.Cover.H - Abs(Button[B].H)) * 0.7 ;
+ Button[B].H := Button[B].W;
+ end;
+ end;
+ end
+ else
+ begin
+ //Change Pos of all Buttons
+ for B := Low(Button) to High(Button) do
+ begin
+ Button[B].Visible := CatSongs.Song[B].Visible; //Adjust Visibility
+ if Button[B].Visible then //Only Change Pos for Visible Buttons
+ begin
+ Pos := (CatSongs.VisibleIndex(B) - SongCurrent);
+ if (Pos < -VS/2) then
+ Pos := Pos + VS
+ else if (Pos > VS/2) then
+ Pos := Pos - VS;
+
+ if (Abs(Pos) < 2.5) then {fixed Positions}
+ begin
+ Angle := Pi * (Pos / 5);
+ //Button[B].Visible := false;
+
+ Button[B].H := Abs(Theme.Song.Cover.H * cos(Angle*0.8));//Power(Z2, 3);
+
+ Button[B].DeSelectReflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;
+
+ Button[B].Z := 0.95 - Abs(Pos) * 0.01;
+
+ Button[B].X := (Theme.Song.Cover.X + (Theme.Song.Cover.H - Abs(Theme.Song.Cover.H * cos(Angle))) * 0.5);
+
+ Button[B].W := Button[B].H;
+
+ Diff := (Button[B].H - Theme.Song.Cover.H)/2;
+
+ X := Sin(Angle*1.3)*0.9;
+
+ Button[B].Y := Theme.Song.Cover.Y + Theme.Song.Cover.W * X - Diff;
+ end
+ else
+ begin {Behind the Front Covers}
+
+ // limit-bg-covers hack
+ if (abs(VS/2-abs(Pos))>10) then
+ Button[B].Visible := false;
+ if VS > 25 then
+ VS:=25;
+ // end of limit-bg-covers hack
+
+ if Pos < 0 then
+ Pos := (Pos - VS/2)/VS
+ else
+ Pos := (Pos + VS/2)/VS;
+
+ Angle := Pi * Pos*2;
+
+ Button[B].Z := (0.4 - Abs(Pos/4)) -0.00001; //z < 0.49999 is behind the cover 1 is in front of the covers
+
+ Button[B].H :=0.6*(Theme.Song.Cover.H-Abs(Theme.Song.Cover.H * cos(Angle/2)*0.8));//Power(Z2, 3);
+
+ Button[B].W := Button[B].H;
+
+ Button[B].X := Theme.Song.Cover.X - (Button[B].H - Theme.Song.Cover.H)*0.5;
+
+ Button[B].DeSelectReflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H;
+
+ Button[B].Y := Theme.Song.Cover.Y+Theme.Song.Cover.H/2-Button[b].H/2+Theme.Song.Cover.W/320*(Theme.Song.Cover.H*sin(Angle/2)*1.52);
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure TScreenSong.OnShow;
+begin
+ inherited;
+{**
+ * Pause background music, so we can play it again on scorescreen
+ *}
+ SoundLib.PauseBgMusic;
+
+ AudioPlayback.Stop;
+ PreviewOpened := -1;
+
+ // reset video playback engine
+ fCurrentVideo := nil;
+
+ if Ini.Players <= 3 then PlayersPlay := Ini.Players + 1;
+ if Ini.Players = 4 then PlayersPlay := 6;
+
+ //Cat Mod etc
+ if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow = -1) then
+ begin
+ CatSongs.ShowCategoryList;
+ FixSelected;
+ //Show Cat in Top Left Mod
+ HideCatTL;
+ end;
+
+ if Length(CatSongs.Song) > 0 then
+ begin
+ SetScroll;
+ end;
+
+ //Playlist Mode
+ if (Mode = smNormal) then
+ begin
+ //If Playlist Shown -> Select Next automatically
+ if (CatSongs.CatNumShow = -3) then
+ begin
+ SelectNext;
+ end;
+ end
+ //Party Mode
+ else if (Mode = smPartyMode) then
+ begin
+ SelectRandomSong;
+ //Show Menu directly in PartyMode
+ //But only if selected in Options
+ if (Ini.PartyPopup = 1) then
+ begin
+ ScreenSongMenu.MenuShow(SM_Party_Main);
+ end;
+ end;
+
+ isScrolling := false;
+ SetJoker;
+ SetStatics;
+end;
+
+procedure TScreenSong.OnShowFinish;
+begin
+ isScrolling := true;
+ CoverTime := 10;
+end;
+
+procedure TScreenSong.OnHide;
+begin
+ // turn music volume to 100%
+ AudioPlayback.SetVolume(1.0);
+
+ // stop preview
+ StopMusicPreview();
+ StopVideoPreview();
+end;
+
+procedure TScreenSong.DrawExtensions;
+begin
+ //Draw Song Menu
+ if (ScreenSongMenu.Visible) then
+ begin
+ ScreenSongMenu.Draw;
+ end
+ else if (ScreenSongJumpto.Visible) then
+ begin
+ ScreenSongJumpto.Draw;
+ end
+end;
+
+function TScreenSong.Draw: boolean;
+var
+ dx: real;
+ dt: real;
+ I: integer;
+ VideoAlpha: real;
+begin
+ if isScrolling then
+ begin
+ dx := SongTarget-SongCurrent;
+ dt := TimeSkip * 7;
+
+ if dt > 1 then
+ dt := 1;
+
+ SongCurrent := SongCurrent + dx*dt;
+
+ if SameValue(SongCurrent, SongTarget, 0.002) and (CatSongs.VisibleSongs > 0) then
+ begin
+ isScrolling := false;
+ SongCurrent := SongTarget;
+ OnSongSelect;
+ end;
+ end;
+
+ {
+ if SongCurrent > Catsongs.VisibleSongs then
+ begin
+ SongCurrent := SongCurrent - Catsongs.VisibleSongs;
+ SongTarget := SongTarget - Catsongs.VisibleSongs;
+ end;
+ }
+
+ //Log.BenchmarkStart(5);
+
+ SetScroll;
+
+ //Log.BenchmarkEnd(5);
+ //Log.LogBenchmark('SetScroll4', 5);
+
+ //Fading Functions, Only if Covertime is under 5 Seconds
+ if (CoverTime < 9) then
+ begin
+ // cover fade
+ if (CoverTime < 1) and (CoverTime + TimeSkip >= 1) then
+ begin
+ // load new texture
+ Texture.GetTexture(Button[Interaction].Texture.Name, TEXTURE_TYPE_PLAIN, false);
+ Button[Interaction].Texture.Alpha := 1;
+ Button[Interaction].Texture2 := Texture.GetTexture(Button[Interaction].Texture.Name, TEXTURE_TYPE_PLAIN, false);
+ Button[Interaction].Texture2.Alpha := 1;
+ end;
+
+ //Update Fading Time
+ CoverTime := CoverTime + TimeSkip;
+
+ //Update Fading Texture
+ Button[Interaction].Texture2.Alpha := (CoverTime - 1) * 1.5;
+ if Button[Interaction].Texture2.Alpha > 1 then
+ Button[Interaction].Texture2.Alpha := 1;
+
+ end;
+
+ //inherited Draw;
+ //heres a little Hack, that causes the Statics
+ //are Drawn after the Buttons because of some Blending Problems.
+ //This should cause no Problems because all Buttons on this screen
+ //Has Z Position.
+ //Draw BG
+ DrawBG;
+
+ VideoAlpha := Button[interaction].Texture.Alpha*(CoverTime-1);
+ //Instead of Draw FG Procedure:
+ //We draw Buttons for our own
+ for I := 0 to Length(Button) - 1 do
+ begin
+ if (I<>Interaction) or not Assigned(fCurrentVideo) or (VideoAlpha<1) or AudioPlayback.Finished then
+ Button[I].Draw;
+ end;
+
+ if AudioPlayback.Finished then
+ StopVideoPreview;
+
+ if Assigned(fCurrentVideo) then
+ begin
+ // Just call this once
+ // when Screens = 2
+ if (ScreenAct = 1) then
+ fCurrentVideo.GetFrame(CatSongs.Song[Interaction].VideoGAP + AudioPlayback.Position);
+
+ fCurrentVideo.SetScreen(ScreenAct);
+ fCurrentVideo.Alpha := VideoAlpha;
+
+ //set up window
+ with Button[interaction] do
+ begin
+ fCurrentVideo.SetScreenPosition(X, Y, Z);
+ fCurrentVideo.Width := W;
+ fCurrentVideo.Height := H;
+ fCurrentVideo.ReflectionSpacing := Reflectionspacing;
+ end;
+ fCurrentVideo.AspectCorrection := acoCrop;
+
+ fCurrentVideo.Draw;
+
+ if Button[interaction].Reflection then
+ fCurrentVideo.DrawReflection;
+ end;
+
+ // Statics
+ for I := 0 to Length(Statics) - 1 do
+ Statics[I].Draw;
+
+ // and texts
+ for I := 0 to Length(Text) - 1 do
+ Text[I].Draw;
+
+ Equalizer.Draw;
+
+ DrawExtensions;
+
+ Result := true;
+end;
+
+procedure TScreenSong.SelectNext;
+var
+ Skip: integer;
+ VS: integer;
+begin
+ VS := CatSongs.VisibleSongs;
+
+ if VS > 0 then
+ begin
+ if (not isScrolling) and (VS > 0) then
+ begin
+ isScrolling := true;
+ OnSongDeselect;
+ end;
+
+ Skip := 1;
+
+ // this 1 could be changed by CatSongs.FindNextVisible
+ while (not CatSongs.Song[(Interaction + Skip) mod Length(Interactions)].Visible) do
+ Inc(Skip);
+
+ SongTarget := SongTarget + 1;//Skip;
+
+ Interaction := (Interaction + Skip) mod Length(Interactions);
+
+ // try to keep all at the beginning
+ if SongTarget > VS-1 then
+ begin
+ SongTarget := SongTarget - VS;
+ SongCurrent := SongCurrent - VS;
+ end;
+
+ end;
+
+ // Interaction -> Button, load cover
+ // show uncached texture
+ //Button[Interaction].Texture := Texture.GetTexture(Button[Interaction].Texture.Name, TEXTURE_TYPE_PLAIN, false);
+end;
+
+procedure TScreenSong.SelectPrev;
+var
+ Skip: integer;
+ VS: integer;
+begin
+ VS := CatSongs.VisibleSongs;
+
+ if VS > 0 then
+ begin
+ if (not isScrolling) and (VS > 0) then
+ begin
+ isScrolling := true;
+ OnSongDeselect;
+ end;
+
+ Skip := 1;
+
+ while (not CatSongs.Song[(Interaction - Skip + Length(Interactions)) mod Length(Interactions)].Visible) do
+ Inc(Skip);
+ SongTarget := SongTarget - 1;//Skip;
+
+ Interaction := (Interaction - Skip + Length(Interactions)) mod Length(Interactions);
+
+ // try to keep all at the beginning
+ if SongTarget < 0 then
+ begin
+ SongTarget := SongTarget + CatSongs.VisibleSongs;
+ SongCurrent := SongCurrent + CatSongs.VisibleSongs;
+ end;
+
+ // show uncached texture
+ //Button[Interaction].Texture := Texture.GetTexture(Button[Interaction].Texture.Name, TEXTURE_TYPE_PLAIN, false);
+ end;
+end;
+
+procedure TScreenSong.StartMusicPreview();
+var
+ Song: TSong;
+ PreviewPos: real;
+begin
+ AudioPlayback.Close();
+
+ if CatSongs.VisibleSongs = 0 then
+ Exit;
+
+ Song := CatSongs.Song[Interaction];
+ if not assigned(Song) then
+ Exit;
+
+ //fix: if main cat than there is nothing to play
+ if Song.main then
+ Exit;
+
+ if AudioPlayback.Open(Song.Path.Append(Song.Mp3)) then
+ begin
+ PreviewOpened := Interaction;
+
+ PreviewPos := AudioPlayback.Length / 4;
+ // fix for invalid music file lengths
+ if (PreviewPos > 60.0) then
+ PreviewPos := 60.0;
+ AudioPlayback.Position := PreviewPos;
+
+ // set preview volume
+ if (Ini.PreviewFading = 0) then
+ begin
+ // music fade disabled: start with full volume
+ AudioPlayback.SetVolume(IPreviewVolumeVals[Ini.PreviewVolume]);
+ AudioPlayback.Play()
+ end
+ else
+ begin
+ // music fade enabled: start muted and fade-in
+ AudioPlayback.SetVolume(0);
+ AudioPlayback.FadeIn(Ini.PreviewFading, IPreviewVolumeVals[Ini.PreviewVolume]);
+ end;
+ end;
+end;
+
+procedure TScreenSong.StopMusicPreview();
+begin
+ // Stop preview of previous song
+ AudioPlayback.Stop;
+end;
+
+procedure TScreenSong.StartVideoPreview();
+var
+ VideoFile: IPath;
+ Song: TSong;
+
+begin
+ if (Ini.VideoPreview=0) then
+ Exit;
+
+ if Assigned(fCurrentVideo) then
+ begin
+ fCurrentVideo.Stop();
+ fCurrentVideo := nil;
+ end;
+
+ //if no audio open => exit
+ if (PreviewOpened = -1) then
+ Exit;
+
+ if CatSongs.VisibleSongs = 0 then
+ Exit;
+
+ Song := CatSongs.Song[Interaction];
+ if not assigned(Song) then
+ Exit;
+
+ //fix: if main cat than there is nothing to play
+ if Song.main then
+ Exit;
+
+ VideoFile := Song.Path.Append(Song.Video);
+ if (Song.Video.IsSet) and VideoFile.IsFile then
+ begin
+ fCurrentVideo := VideoPlayback.Open(VideoFile);
+ if (fCurrentVideo <> nil) then
+ begin
+ fCurrentVideo.Position := Song.VideoGAP + AudioPlayback.Position;
+ fCurrentVideo.Play;
+ end;
+ end;
+end;
+
+procedure TScreenSong.StopVideoPreview();
+begin
+ // Stop video preview of previous song
+ if Assigned(fCurrentVideo) then
+ begin
+ fCurrentVideo.Stop();
+ fCurrentVideo := nil;
+ end;
+end;
+
+// Changes previewed song
+procedure TScreenSong.ChangeMusic;
+begin
+ StopMusicPreview();
+ StopVideoPreview();
+ PreviewOpened := -1;
+ StartMusicPreview();
+ StartVideoPreview();
+end;
+
+procedure TScreenSong.SkipTo(Target: cardinal);
+var
+ i: integer;
+begin
+ Interaction := High(CatSongs.Song);
+ SongTarget := 0;
+
+ for i := 1 to Target+1 do
+ SelectNext;
+
+ FixSelected2;
+end;
+
+procedure TScreenSong.SelectRandomSong;
+var
+ I, I2: integer;
+begin
+ case PlaylistMan.Mode of
+ smNormal: // all songs just select random song
+ begin
+ // when tabs are activated then use tab method
+ if (Ini.TabsAtStartup = 1) then
+ begin
+ repeat
+ I2 := Low(CatSongs.Song) + Random(High(CatSongs.Song) + 1 - Low(CatSongs.Song));
+ until CatSongs.Song[I2].Main = false;
+
+ // search cat
+ for I := I2 downto Low(CatSongs.Song) do
+ begin
+ if CatSongs.Song[I].Main then
+ break;
+ end;
+ // I is the cat number, I2 is the no of the song within this cat
+
+ // choose cat
+ CatSongs.ShowCategoryList;
+
+ // show cat in top left mod
+ ShowCatTL(I);
+
+ CatSongs.ClickCategoryButton(I);
+ SelectNext;
+
+ // choose song
+ SkipTo(I2 - I);
+ end
+ // when tabs are deactivated use easy method
+ else
+ SkipTo(Random(CatSongs.VisibleSongs));
+ end;
+ smPartyMode: // one category select category and select random song
+ begin
+ CatSongs.ShowCategoryList;
+ CatSongs.ClickCategoryButton(PlaylistMan.CurPlayList);
+ ShowCatTL(PlaylistMan.CurPlayList);
+
+ SelectNext;
+ FixSelected2;
+
+ SkipTo(Random(CatSongs.VisibleSongs));
+ end;
+ smPlaylistRandom: // playlist: select playlist and select random song
+ begin
+ PlaylistMan.SetPlayList(PlaylistMan.CurPlayList);
+
+ SkipTo(Random(CatSongs.VisibleSongs));
+ FixSelected2;
+ end;
+ end;
+
+ AudioPlayback.PlaySound(SoundLib.Change);
+ SetScroll;
+end;
+
+procedure TScreenSong.SetJoker;
+begin
+ // If Party Mode
+ if Mode = smPartyMode then //Show Joker that are available
+ begin
+ if (Length(Party.Teams) >= 1) then
+ begin
+ Statics[StaticTeam1Joker1].Visible := (Party.Teams[0].JokersLeft >= 1);
+ Statics[StaticTeam1Joker2].Visible := (Party.Teams[0].JokersLeft >= 2);
+ Statics[StaticTeam1Joker3].Visible := (Party.Teams[0].JokersLeft >= 3);
+ Statics[StaticTeam1Joker4].Visible := (Party.Teams[0].JokersLeft >= 4);
+ Statics[StaticTeam1Joker5].Visible := (Party.Teams[0].JokersLeft >= 5);
+ end
+ else
+ begin
+ Statics[StaticTeam1Joker1].Visible := false;
+ Statics[StaticTeam1Joker2].Visible := false;
+ Statics[StaticTeam1Joker3].Visible := false;
+ Statics[StaticTeam1Joker4].Visible := false;
+ Statics[StaticTeam1Joker5].Visible := false;
+ end;
+
+ if (Length(Party.Teams) >= 2) then
+ begin
+ Statics[StaticTeam2Joker1].Visible := (Party.Teams[1].JokersLeft >= 1);
+ Statics[StaticTeam2Joker2].Visible := (Party.Teams[1].JokersLeft >= 2);
+ Statics[StaticTeam2Joker3].Visible := (Party.Teams[1].JokersLeft >= 3);
+ Statics[StaticTeam2Joker4].Visible := (Party.Teams[1].JokersLeft >= 4);
+ Statics[StaticTeam2Joker5].Visible := (Party.Teams[1].JokersLeft >= 5);
+ end
+ else
+ begin
+ Statics[StaticTeam2Joker1].Visible := false;
+ Statics[StaticTeam2Joker2].Visible := false;
+ Statics[StaticTeam2Joker3].Visible := false;
+ Statics[StaticTeam2Joker4].Visible := false;
+ Statics[StaticTeam2Joker5].Visible := false;
+ end;
+
+ if (Length(Party.Teams) >= 3) then
+ begin
+ Statics[StaticTeam3Joker1].Visible := (Party.Teams[2].JokersLeft >= 1);
+ Statics[StaticTeam3Joker2].Visible := (Party.Teams[2].JokersLeft >= 2);
+ Statics[StaticTeam3Joker3].Visible := (Party.Teams[2].JokersLeft >= 3);
+ Statics[StaticTeam3Joker4].Visible := (Party.Teams[2].JokersLeft >= 4);
+ Statics[StaticTeam3Joker5].Visible := (Party.Teams[2].JokersLeft >= 5);
+ end
+ else
+ begin
+ Statics[StaticTeam3Joker1].Visible := false;
+ Statics[StaticTeam3Joker2].Visible := false;
+ Statics[StaticTeam3Joker3].Visible := false;
+ Statics[StaticTeam3Joker4].Visible := false;
+ Statics[StaticTeam3Joker5].Visible := false;
+ end;
+ end
+ else
+ begin //Hide all
+ Statics[StaticTeam1Joker1].Visible := false;
+ Statics[StaticTeam1Joker2].Visible := false;
+ Statics[StaticTeam1Joker3].Visible := false;
+ Statics[StaticTeam1Joker4].Visible := false;
+ Statics[StaticTeam1Joker5].Visible := false;
+
+ Statics[StaticTeam2Joker1].Visible := false;
+ Statics[StaticTeam2Joker2].Visible := false;
+ Statics[StaticTeam2Joker3].Visible := false;
+ Statics[StaticTeam2Joker4].Visible := false;
+ Statics[StaticTeam2Joker5].Visible := false;
+
+ Statics[StaticTeam3Joker1].Visible := false;
+ Statics[StaticTeam3Joker2].Visible := false;
+ Statics[StaticTeam3Joker3].Visible := false;
+ Statics[StaticTeam3Joker4].Visible := false;
+ Statics[StaticTeam3Joker5].Visible := false;
+ end;
+end;
+
+procedure TScreenSong.SetStatics;
+var
+ I: integer;
+ Visible: boolean;
+begin
+ //Set Visibility of Party Statics and Text
+ Visible := (Mode = smPartyMode);
+
+ for I := 0 to High(StaticParty) do
+ Statics[StaticParty[I]].Visible := Visible;
+
+ for I := 0 to High(TextParty) do
+ Text[TextParty[I]].Visible := Visible;
+
+ //Set Visibility of Non Party Statics and Text
+ Visible := not Visible;
+
+ for I := 0 to High(StaticNonParty) do
+ Statics[StaticNonParty[I]].Visible := Visible;
+
+ for I := 0 to High(TextNonParty) do
+ Text[TextNonParty[I]].Visible := Visible;
+end;
+
+//Procedures for Menu
+
+procedure TScreenSong.StartSong;
+begin
+ CatSongs.Selected := Interaction;
+ StopMusicPreview();
+
+ //Party Mode
+ if (Mode = smPartyMode) then
+ begin
+ FadeTo(@ScreenSing);
+ end
+ else
+ begin
+ FadeTo(@ScreenSing);
+ end;
+end;
+
+procedure TScreenSong.SelectPlayers;
+begin
+ CatSongs.Selected := Interaction;
+ StopMusicPreview();
+
+ ScreenName.Goto_SingScreen := true;
+ FadeTo(@ScreenName);
+end;
+
+procedure TScreenSong.OpenEditor;
+begin
+ if (Songs.SongList.Count > 0) and
+ (not CatSongs.Song[Interaction].Main) and
+ (Mode = smNormal) then
+ begin
+ StopMusicPreview();
+ AudioPlayback.PlaySound(SoundLib.Start);
+ CurrentSong := CatSongs.Song[Interaction];
+ FadeTo(@ScreenEditSub);
+ end;
+end;
+
+//Team No of Team (0-5)
+procedure TScreenSong.DoJoker (Team: integer);
+begin
+ if (Mode = smPartyMode) and
+ (High(Party.Teams) >= Team) and
+ (Party.Teams[Team].JokersLeft > 0) then
+ begin
+ //Use Joker
+ Dec(Party.Teams[Team].JokersLeft);
+ SelectRandomSong;
+ SetJoker;
+ end;
+end;
+
+//Detailed Cover Unloading. Unloads the Detailed, uncached Cover of the cur. Song
+procedure TScreenSong.UnloadDetailedCover;
+begin
+ // show cached texture
+ Button[Interaction].Texture := Texture.GetTexture(Button[Interaction].Texture.Name, TEXTURE_TYPE_PLAIN, true);
+ Button[Interaction].Texture2.Alpha := 0;
+
+ if Button[Interaction].Texture.Name <> Skin.GetTextureFileName('SongCover') then
+ Texture.UnloadTexture(Button[Interaction].Texture.Name, TEXTURE_TYPE_PLAIN, false);
+end;
+
+procedure TScreenSong.Refresh;
+begin
+ {
+ CatSongs.Refresh;
+ CatSongs.ShowCategoryList;
+ Interaction := 0;
+ SelectNext(true);
+ FixSelected;
+ }
+end;
+
+end.
diff --git a/src/screens/UScreenSongJumpto.pas b/src/screens/UScreenSongJumpto.pas
new file mode 100644
index 00000000..b3d48679
--- /dev/null
+++ b/src/screens/UScreenSongJumpto.pas
@@ -0,0 +1,247 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenSongJumpto;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ SysUtils,
+ UMenu,
+ UDisplay,
+ UMusic,
+ UFiles,
+ USongs,
+ UThemes;
+
+type
+ TScreenSongJumpto = class(TMenu)
+ private
+ //For ChangeMusic
+ fLastPlayed: integer;
+ fVisible: boolean;
+ fSelectType: TSongFilter;
+ fVisSongs: integer;
+
+ procedure SetTextFound(Count: Cardinal);
+
+ //Visible //Whether the Menu should be Drawn
+ //Whether the Menu should be Drawn
+ procedure SetVisible(Value: boolean);
+ public
+ constructor Create; override;
+
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ function Draw: boolean; override;
+
+ property Visible: boolean read fVisible write SetVisible;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UIni,
+ UTexture,
+ ULanguage,
+ UParty,
+ UScreenSong,
+ ULog,
+ UUnicodeUtils;
+
+function TScreenSongJumpto.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ if (IsAlphaNumericChar(CharCode) or
+ IsPunctuationChar(CharCode)) then
+ begin
+ if (Interaction = 0) then
+ begin
+ Button[0].Text[0].Text := Button[0].Text[0].Text + UCS4ToUTF8String(CharCode);
+ SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, fSelectType));
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_BACKSPACE:
+ begin
+ if (Interaction = 0) and (Length(Button[0].Text[0].Text) > 0) then
+ begin
+ Button[0].Text[0].DeleteLastLetter();
+ SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, fSelectType));
+ end;
+ end;
+
+ SDLK_RETURN,
+ SDLK_ESCAPE:
+ begin
+ Visible := false;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ if (fVisSongs = 0) and (Length(Button[0].Text[0].Text) > 0) then
+ begin
+ ScreenSong.UnLoadDetailedCover;
+ Button[0].Text[0].Text := '';
+ CatSongs.SetFilter('', fltAll);
+ SetTextFound(0);
+ end;
+ end;
+
+ SDLK_DOWN:
+ begin
+ {SelectNext;
+ Button[0].Text[0].Selected := (Interaction = 0);}
+ end;
+
+ SDLK_UP:
+ begin
+ {SelectPrev;
+ Button[0].Text[0].Selected := (Interaction = 0); }
+ end;
+
+ SDLK_RIGHT:
+ begin
+ Interaction := 1;
+ InteractInc;
+ if (Length(Button[0].Text[0].Text) > 0) then
+ SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, fSelectType));
+ Interaction := 0;
+ end;
+ SDLK_LEFT:
+ begin
+ Interaction := 1;
+ InteractDec;
+ if (Length(Button[0].Text[0].Text) > 0) then
+ SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, fSelectType));
+ Interaction := 0;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenSongJumpto.Create;
+begin
+ inherited Create;
+
+ AddText(Theme.SongJumpto.TextFound);
+
+ LoadFromTheme(Theme.SongJumpto);
+
+ AddButton(Theme.SongJumpto.ButtonSearchText);
+ if (Length(Button[0].Text) = 0) then
+ AddButtonText(14, 20, '');
+
+ fSelectType := fltAll;
+ AddSelectSlide(Theme.SongJumpto.SelectSlideType, PInteger(@fSelectType)^, Theme.SongJumpto.IType);
+
+ Interaction := 0;
+ fLastPlayed := 0;
+end;
+
+procedure TScreenSongJumpto.SetVisible(Value: boolean);
+begin
+//If change from invisible to Visible then OnShow
+ if (fVisible = false) and (Value = true) then
+ OnShow;
+
+ fVisible := Value;
+end;
+
+procedure TScreenSongJumpto.OnShow;
+begin
+ inherited;
+
+ //Reset Screen if no Old Search is Displayed
+ if (CatSongs.CatNumShow <> -2) then
+ begin
+ SelectsS[0].SetSelectOpt(0);
+
+ Button[0].Text[0].Text := '';
+ Text[0].Text := Theme.SongJumpto.NoSongsFound;
+ end;
+
+ //Select Input
+ Interaction := 0;
+ Button[0].Text[0].Selected := true;
+
+ fLastPlayed := ScreenSong.Interaction;
+end;
+
+function TScreenSongJumpto.Draw: boolean;
+begin
+ Result := inherited Draw;
+end;
+
+procedure TScreenSongJumpto.SetTextFound(Count: cardinal);
+begin
+ if (Count = 0) then
+ begin
+ Text[0].Text := Theme.SongJumpto.NoSongsFound;
+ if (Length(Button[0].Text[0].Text) = 0) then
+ ScreenSong.HideCatTL
+ else
+ ScreenSong.ShowCatTLCustom(Format(Theme.SongJumpto.CatText, [Button[0].Text[0].Text]));
+ end
+ else
+ begin
+ Text[0].Text := Format(Theme.SongJumpto.SongsFound, [Count]);
+
+ //Set CatTopLeftText
+ ScreenSong.ShowCatTLCustom(Format(Theme.SongJumpto.CatText, [Button[0].Text[0].Text]));
+ end;
+
+ //Set visSongs
+ fVisSongs := Count;
+
+ //Fix SongSelection
+ ScreenSong.Interaction := high(CatSongs.Song);
+ ScreenSong.SelectNext;
+ ScreenSong.FixSelected;
+
+ //Play Correct Music
+ if (ScreenSong.Interaction <> fLastPlayed) or (CatSongs.VisibleSongs = 0) then
+ begin
+ if (CatSongs.VisibleSongs > 0) then
+ fLastPlayed := ScreenSong.Interaction
+ else
+ fLastPlayed := -1;
+
+ ScreenSong.ChangeMusic;
+ end;
+end;
+
+end.
diff --git a/src/screens/UScreenSongMenu.pas b/src/screens/UScreenSongMenu.pas
new file mode 100644
index 00000000..173ac2c8
--- /dev/null
+++ b/src/screens/UScreenSongMenu.pas
@@ -0,0 +1,664 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenSongMenu;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ UDisplay,
+ UMusic,
+ UFiles,
+ SysUtils,
+ UThemes;
+
+type
+ TScreenSongMenu = class(TMenu)
+ private
+ CurMenu: byte; // num of the cur. shown menu
+ public
+ Visible: boolean; // whether the menu should be drawn
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ function Draw: boolean; override;
+ procedure MenuShow(sMenu: byte);
+ procedure HandleReturn;
+ end;
+
+const
+ SM_Main = 1;
+
+ SM_PlayList = 64 or 1;
+ SM_Playlist_Add = 64 or 2;
+ SM_Playlist_New = 64 or 3;
+
+ SM_Playlist_DelItem = 64 or 5;
+
+ SM_Playlist_Load = 64 or 8 or 1;
+ SM_Playlist_Del = 64 or 8 or 5;
+
+ SM_Party_Main = 128 or 1;
+ SM_Party_Joker = 128 or 2;
+
+var
+ ISelections: array of UTF8String;
+ SelectValue: integer;
+
+implementation
+
+uses
+ UGraphic,
+ UMain,
+ UIni,
+ UTexture,
+ ULanguage,
+ UParty,
+ UPlaylist,
+ USongs,
+ UUnicodeUtils;
+
+function TScreenSongMenu.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // key down
+ if (CurMenu = SM_Playlist_New) and (Interaction=0) then
+ begin
+ // check normal keys
+ if IsAlphaNumericChar(CharCode) or
+ (CharCode in [Ord(' '), Ord('-'), Ord('_'), Ord('!'),
+ Ord(','), Ord('<'), Ord('/'), Ord('*'),
+ Ord('?'), Ord(''''), Ord('"')]) then
+ begin
+ Button[Interaction].Text[0].Text := Button[Interaction].Text[0].Text +
+ UCS4ToUTF8String(CharCode);
+ exit;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_BACKSPACE:
+ begin
+ Button[Interaction].Text[0].DeleteLastLetter;
+ exit;
+ end;
+ end;
+ end;
+
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE:
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ Visible := false;
+ end;
+
+ SDLK_RETURN:
+ begin
+ HandleReturn;
+ end;
+
+ SDLK_DOWN: InteractNext;
+ SDLK_UP: InteractPrev;
+
+ SDLK_RIGHT:
+ begin
+ if (Interaction=3) then
+ InteractInc;
+ end;
+ SDLK_LEFT:
+ begin
+ if (Interaction=3) then
+ InteractDec;
+ end;
+
+ SDLK_1:
+ begin // jocker
+ // use joker
+ case CurMenu of
+ SM_Party_Main:
+ begin
+ ScreenSong.DoJoker(0)
+ end;
+ end;
+ end;
+ SDLK_2:
+ begin // jocker
+ // use joker
+ case CurMenu of
+ SM_Party_Main:
+ begin
+ ScreenSong.DoJoker(1)
+ end;
+ end;
+ end;
+ SDLK_3:
+ begin // jocker
+ // use joker
+ case CurMenu of
+ SM_Party_Main:
+ begin
+ ScreenSong.DoJoker(2)
+ end;
+ end;
+ end;
+ end; // case
+ end; // if
+end;
+
+constructor TScreenSongMenu.Create;
+begin
+ inherited Create;
+
+ // create dummy selectslide entrys
+ SetLength(ISelections, 1);
+ ISelections[0] := 'Dummy';
+
+ AddText(Theme.SongMenu.TextMenu);
+
+ LoadFromTheme(Theme.SongMenu);
+
+ AddButton(Theme.SongMenu.Button1);
+ if (Length(Button[0].Text) = 0) then
+ AddButtonText(14, 20, 'Button 1');
+
+ AddButton(Theme.SongMenu.Button2);
+ if (Length(Button[1].Text) = 0) then
+ AddButtonText(14, 20, 'Button 2');
+
+ AddButton(Theme.SongMenu.Button3);
+ if (Length(Button[2].Text) = 0) then
+ AddButtonText(14, 20, 'Button 3');
+
+ AddSelectSlide(Theme.SongMenu.SelectSlide3, SelectValue, ISelections);
+
+ AddButton(Theme.SongMenu.Button4);
+ if (Length(Button[3].Text) = 0) then
+ AddButtonText(14, 20, 'Button 4');
+
+ Interaction := 0;
+end;
+
+function TScreenSongMenu.Draw: boolean;
+begin
+ Result := inherited Draw;
+end;
+
+procedure TScreenSongMenu.OnShow;
+begin
+ inherited;
+end;
+
+procedure TScreenSongMenu.MenuShow(sMenu: byte);
+begin
+ Interaction := 0; // reset interaction
+ Visible := true; // set visible
+ case sMenu of
+ SM_Main:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_MAIN');
+
+ Button[0].Visible := true;
+ Button[1].Visible := true;
+ Button[2].Visible := true;
+ Button[3].Visible := true;
+ SelectsS[0].Visible := false;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAY');
+ Button[1].Text[0].Text := Language.Translate('SONG_MENU_CHANGEPLAYERS');
+ Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_ADD');
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_EDIT');
+ end;
+
+ SM_PlayList:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST');
+
+ Button[0].Visible := true;
+ Button[1].Visible := true;
+ Button[2].Visible := true;
+ Button[3].Visible := true;
+ SelectsS[0].Visible := false;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAY');
+ Button[1].Text[0].Text := Language.Translate('SONG_MENU_CHANGEPLAYERS');
+ Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_DEL');
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_EDIT');
+ end;
+
+ SM_Playlist_Add:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_ADD');
+
+ Button[0].Visible := true;
+ Button[1].Visible := false;
+ Button[2].Visible := false;
+ Button[3].Visible := true;
+ SelectsS[0].Visible := true;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_ADD_NEW');
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_ADD_EXISTING');
+
+ SetLength(ISelections, Length(PlaylistMan.Playlists));
+ PlaylistMan.GetNames(ISelections);
+
+ if (Length(ISelections)>=1) then
+ begin
+ UpdateSelectSlideOptions(Theme.SongMenu.SelectSlide3, 0, ISelections, SelectValue);
+ end
+ else
+ begin
+ Button[3].Visible := false;
+ SelectsS[0].Visible := false;
+ Button[2].Visible := true;
+ Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_NOEXISTING');
+ end;
+ end;
+
+ SM_Playlist_New:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_NEW');
+
+ Button[0].Visible := true;
+ Button[1].Visible := false;
+ Button[2].Visible := true;
+ Button[3].Visible := true;
+ SelectsS[0].Visible := false;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_NEW_UNNAMED');
+ Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_NEW_CREATE');
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_CANCEL');
+ end;
+
+ SM_Playlist_DelItem:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_DELITEM');
+
+ Button[0].Visible := true;
+ Button[1].Visible := false;
+ Button[2].Visible := false;
+ Button[3].Visible := true;
+ SelectsS[0].Visible := false;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_YES');
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_CANCEL');
+ end;
+
+ SM_Playlist_Load:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_LOAD');
+
+ // show delete curent playlist button when playlist is opened
+ Button[0].Visible := (CatSongs.CatNumShow = -3);
+
+ Button[1].Visible := false;
+ Button[2].Visible := false;
+ Button[3].Visible := true;
+ SelectsS[0].Visible := true;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_DELCURRENT');
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_LOAD');
+
+ SetLength(ISelections, Length(PlaylistMan.Playlists));
+ PlaylistMan.GetNames(ISelections);
+
+ if (Length(ISelections)>=1) then
+ begin
+ UpdateSelectSlideOptions(Theme.SongMenu.SelectSlide3, 0, ISelections, SelectValue);
+ Interaction := 3;
+ end
+ else
+ begin
+ Button[3].Visible := false;
+ SelectsS[0].Visible := false;
+ Button[2].Visible := true;
+ Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_NOEXISTING');
+ Interaction := 2;
+ end;
+ end;
+
+ SM_Playlist_Del:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_DEL');
+
+ Button[0].Visible := true;
+ Button[1].Visible := false;
+ Button[2].Visible := false;
+ Button[3].Visible := true;
+ SelectsS[0].Visible := false;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_YES');
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_CANCEL');
+ end;
+
+ SM_Party_Main:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_PARTY_MAIN');
+
+ Button[0].Visible := true;
+ Button[1].Visible := false;
+ Button[2].Visible := false;
+ Button[3].Visible := true;
+ SelectsS[0].Visible := false;
+
+ Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAY');
+ //Button[1].Text[0].Text := Language.Translate('SONG_MENU_JOKER');
+ //Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYMODI');
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_JOKER');
+ end;
+
+ SM_Party_Joker:
+ begin
+ CurMenu := sMenu;
+ Text[0].Text := Language.Translate('SONG_MENU_NAME_PARTY_JOKER');
+ // to-do : Party
+ Button[0].Visible := (Length(Party.Teams) >= 1) AND (Party.Teams[0].JokersLeft > 0);
+ Button[1].Visible := (Length(Party.Teams) >= 2) AND (Party.Teams[1].JokersLeft > 0);
+ Button[2].Visible := (Length(Party.Teams) >= 3) AND (Party.Teams[2].JokersLeft > 0);
+ Button[3].Visible := True;
+ SelectsS[0].Visible := False;
+
+ if (Button[0].Visible) then
+ Button[0].Text[0].Text := UTF8String(Party.Teams[0].Name);
+ if (Button[1].Visible) then
+ Button[1].Text[0].Text := UTF8String(Party.Teams[1].Name);
+ if (Button[2].Visible) then
+ Button[2].Text[0].Text := UTF8String(Party.Teams[2].Name);
+ Button[3].Text[0].Text := Language.Translate('SONG_MENU_CANCEL');
+
+ // set right interaction
+ if (not Button[0].Visible) then
+ begin
+ if (not Button[1].Visible) then
+ begin
+ if (not Button[2].Visible) then
+ Interaction := 4
+ else
+ Interaction := 2;
+ end
+ else
+ Interaction := 1;
+ end;
+
+ end;
+ end;
+end;
+
+procedure TScreenSongMenu.HandleReturn;
+begin
+ case CurMenu of
+ SM_Main:
+ begin
+ case Interaction of
+ 0: // button 1
+ begin
+ ScreenSong.StartSong;
+ Visible := false;
+ end;
+
+ 1: // button 2
+ begin
+ // select new players then sing:
+ ScreenSong.SelectPlayers;
+ Visible := false;
+ end;
+
+ 2: // button 3
+ begin
+ // show add to playlist menu
+ MenuShow(SM_Playlist_Add);
+ end;
+
+ 3: // selectslide 3
+ begin
+ //Dummy
+ end;
+
+ 4: // button 4
+ begin
+ ScreenSong.OpenEditor;
+ Visible := false;
+ end;
+ end;
+ end;
+
+ SM_PlayList:
+ begin
+ Visible := false;
+ case Interaction of
+ 0: // button 1
+ begin
+ ScreenSong.StartSong;
+ Visible := false;
+ end;
+
+ 1: // button 2
+ begin
+ // select new players then sing:
+ ScreenSong.SelectPlayers;
+ Visible := false;
+ end;
+
+ 2: // button 3
+ begin
+ // show add to playlist menu
+ MenuShow(SM_Playlist_DelItem);
+ end;
+
+ 3: // selectslide 3
+ begin
+ // dummy
+ end;
+
+ 4: // button 4
+ begin
+ ScreenSong.OpenEditor;
+ Visible := false;
+ end;
+ end;
+ end;
+
+ SM_Playlist_Add:
+ begin
+ case Interaction of
+ 0: // button 1
+ begin
+ MenuShow(SM_Playlist_New);
+ end;
+
+ 3: // selectslide 3
+ begin
+ // dummy
+ end;
+
+ 4: // button 4
+ begin
+ PlaylistMan.AddItem(ScreenSong.Interaction, SelectValue);
+ Visible := false;
+ end;
+ end;
+ end;
+
+ SM_Playlist_New:
+ begin
+ case Interaction of
+ 0: // button 1
+ begin
+ // nothing, button for entering name
+ end;
+
+ 2: // button 3
+ begin
+ // create playlist and add song
+ PlaylistMan.AddItem(
+ ScreenSong.Interaction,
+ PlaylistMan.AddPlaylist(Button[0].Text[0].Text));
+ Visible := false;
+ end;
+
+ 3: // selectslide 3
+ begin
+ // cancel -> go back to add screen
+ MenuShow(SM_Playlist_Add);
+ end;
+
+ 4: // button 4
+ begin
+ Visible := false;
+ end;
+ end;
+ end;
+
+ SM_Playlist_DelItem:
+ begin
+ Visible := false;
+ case Interaction of
+ 0: // button 1
+ begin
+ // delete
+ PlayListMan.DelItem(PlayListMan.GetIndexbySongID(ScreenSong.Interaction));
+ Visible := false;
+ end;
+
+ 4: // button 4
+ begin
+ MenuShow(SM_Playlist);
+ end;
+ end;
+ end;
+
+ SM_Playlist_Load:
+ begin
+ case Interaction of
+ 0: // button 1 (Delete playlist)
+ begin
+ MenuShow(SM_Playlist_Del);
+ end;
+ 4: // button 4
+ begin
+ // load playlist
+ PlaylistMan.SetPlayList(SelectValue);
+ Visible := false;
+ end;
+ end;
+ end;
+
+ SM_Playlist_Del:
+ begin
+ Visible := false;
+ case Interaction of
+ 0: // button 1
+ begin
+ // delete
+ PlayListMan.DelPlaylist(PlaylistMan.CurPlayList);
+ Visible := false;
+ end;
+
+ 4: // button 4
+ begin
+ MenuShow(SM_Playlist_Load);
+ end;
+ end;
+ end;
+
+ SM_Party_Main:
+ begin
+ case Interaction of
+ 0: // button 1
+ begin
+ // start singing
+ Party.CallAfterSongSelect;
+ Visible := false;
+ end;
+
+ 4: // button 4
+ begin
+ // joker
+ MenuShow(SM_Party_Joker);
+ end;
+ end;
+ end;
+
+ SM_Party_Joker:
+ begin
+ Visible := false;
+ case Interaction of
+ 0: // button 1
+ begin
+ // joker team 1
+ ScreenSong.DoJoker(0);
+ end;
+
+ 1: // button 2
+ begin
+ // joker team 2
+ ScreenSong.DoJoker(1);
+ end;
+
+ 2: // button 3
+ begin
+ // joker team 3
+ ScreenSong.DoJoker(2);
+ end;
+
+ 4: // button 4
+ begin
+ // cancel... (go back to old menu)
+ MenuShow(SM_Party_Main);
+ end;
+ end;
+ end;
+ end;
+end;
+
+end.
diff --git a/src/screens/UScreenStatDetail.pas b/src/screens/UScreenStatDetail.pas
new file mode 100644
index 00000000..1638cd85
--- /dev/null
+++ b/src/screens/UScreenStatDetail.pas
@@ -0,0 +1,303 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenStatDetail;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ SysUtils,
+ UDisplay,
+ UMusic,
+ UIni,
+ UDataBase,
+ UThemes;
+
+type
+ TScreenStatDetail = class(TMenu)
+ public
+ Typ: TStatType;
+ Page: cardinal;
+ Count: byte;
+ Reversed: boolean;
+
+ TotEntrys: cardinal;
+ TotPages: cardinal;
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+
+ procedure SetTitle;
+ Procedure SetPage(NewPage: cardinal);
+ end;
+
+implementation
+
+uses
+ Math,
+ Classes,
+ UGraphic,
+ ULanguage,
+ ULog,
+ UUnicodeUtils;
+
+function TScreenStatDetail.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenStatMain);
+ end;
+ SDLK_RETURN:
+ begin
+ if Interaction = 0 then
+ begin
+ //Next Page
+ SetPage(Page+1);
+ end;
+
+ if Interaction = 1 then
+ begin
+ //Previous Page
+ if (Page > 0) then
+ SetPage(Page-1);
+ end;
+
+ if Interaction = 2 then
+ begin
+ //Reverse Order
+ Reversed := not Reversed;
+ SetPage(Page);
+ end;
+
+ if Interaction = 3 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenStatMain);
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ InteractPrev;
+ end;
+ SDLK_RIGHT:
+ begin
+ InteractNext;
+ end;
+ SDLK_UP:
+ begin
+ InteractPrev;
+ end;
+ SDLK_DOWN:
+ begin
+ InteractNext;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenStatDetail.Create;
+var
+ I: integer;
+begin
+ inherited Create;
+
+ for I := 0 to High(Theme.StatDetail.TextList) do
+ AddText(Theme.StatDetail.TextList[I]);
+
+ Count := Length(Theme.StatDetail.TextList);
+
+ AddText(Theme.StatDetail.TextDescription);
+ AddText(Theme.StatDetail.TextPage);
+
+ LoadFromTheme(Theme.StatDetail);
+
+ AddButton(Theme.StatDetail.ButtonNext);
+ if (Length(Button[0].Text)=0) then
+ AddButtonText(14, 20, Language.Translate('STAT_NEXT'));
+
+ AddButton(Theme.StatDetail.ButtonPrev);
+ if (Length(Button[1].Text)=0) then
+ AddButtonText(14, 20, Language.Translate('STAT_PREV'));
+
+ AddButton(Theme.StatDetail.ButtonReverse);
+ if (Length(Button[2].Text)=0) then
+ AddButtonText(14, 20, Language.Translate('STAT_REVERSE'));
+
+ AddButton(Theme.StatDetail.ButtonExit);
+ if (Length(Button[3].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[7]);
+
+ Interaction := 0;
+ Typ := TStatType(0);
+end;
+
+procedure TScreenStatDetail.OnShow;
+begin
+ inherited;
+
+ //Set Tot Entrys and PAges
+ TotEntrys := DataBase.GetTotalEntrys(Typ);
+ TotPages := Ceil(TotEntrys / Count);
+
+ //Show correct Title
+ SetTitle;
+
+ //Show First Page
+ Reversed := false;
+ SetPage(0);
+end;
+
+procedure TScreenStatDetail.SetTitle;
+begin
+ if Reversed then
+ Text[Count].Text := Theme.StatDetail.DescriptionR[Ord(Typ)]
+ else
+ Text[Count].Text := Theme.StatDetail.Description[Ord(Typ)];
+end;
+
+procedure TScreenStatDetail.SetPage(NewPage: cardinal);
+var
+ StatList: TList;
+ I: integer;
+ FormatStr: string;
+ PerPage: byte;
+begin
+ // fetch statistics
+ StatList := Database.GetStats(Typ, Count, NewPage, Reversed);
+ if ((StatList <> nil) and (StatList.Count > 0)) then
+ begin
+ Page := NewPage;
+
+ // reset texts
+ for I := 0 to Count-1 do
+ Text[I].Text := '';
+
+ FormatStr := Theme.StatDetail.FormatStr[Ord(Typ)];
+
+ //refresh Texts
+ for I := 0 to StatList.Count-1 do
+ begin
+ try
+ case Typ of
+ stBestScores: begin //Best Scores
+ with TStatResultBestScores(StatList[I]) do
+ begin
+ //Set Texts
+ if (Score > 0) then
+ begin
+ Text[I].Text := Format(FormatStr,
+ [Singer, Score, Theme.ILevel[Difficulty], SongArtist, SongTitle, Date]);
+ end;
+ end;
+ end;
+
+ stBestSingers: begin //Best Singers
+ with TStatResultBestSingers(StatList[I]) do
+ begin
+ //Set Texts
+ if (AverageScore > 0) then
+ Text[I].Text := Format(FormatStr, [Player, AverageScore]);
+ end;
+ end;
+
+ stMostSungSong: begin //Popular Songs
+ with TStatResultMostSungSong(StatList[I]) do
+ begin
+ //Set Texts
+ if (Artist <> '') then
+ Text[I].Text := Format(FormatStr, [Artist, Title, TimesSung]);
+ end;
+ end;
+
+ stMostPopBand: begin //Popular Bands
+ with TStatResultMostPopBand(StatList[I]) do
+ begin
+ //Set Texts
+ if (ArtistName <> '') then
+ Text[I].Text := Format(FormatStr, [ArtistName, TimesSungtot]);
+ end;
+ end;
+ end;
+ except
+ on E: EConvertError do
+ Log.LogError('Error Parsing FormatString in UScreenStatDetail: ' + E.Message);
+ end;
+ end;
+
+ if (Page + 1 = TotPages) and (TotEntrys mod Count <> 0) then
+ PerPage := (TotEntrys mod Count)
+ else
+ PerPage := Count;
+
+ try
+ Text[Count+1].Text := Format(Theme.StatDetail.PageStr,
+ [Page + 1, TotPages, PerPage, TotEntrys]);
+ except
+ on E: EConvertError do
+ Log.LogError('Error Parsing FormatString in UScreenStatDetail: ' + E.Message);
+ end;
+
+ //Show correct Title
+ SetTitle;
+ end;
+
+ Database.FreeStats(StatList);
+end;
+
+procedure TScreenStatDetail.SetAnimationProgress(Progress: real);
+var
+ I: integer;
+begin
+ for I := 0 to High(Button) do
+ Button[I].Texture.ScaleW := Progress;
+end;
+
+end.
diff --git a/src/screens/UScreenStatMain.pas b/src/screens/UScreenStatMain.pas
new file mode 100644
index 00000000..204f40cd
--- /dev/null
+++ b/src/screens/UScreenStatMain.pas
@@ -0,0 +1,323 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenStatMain;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ UMenu,
+ SDL,
+ SysUtils,
+ UDisplay,
+ UMusic,
+ UIni,
+ UThemes;
+
+type
+ TScreenStatMain = class(TMenu)
+ private
+ //Some Stat Value that don't need to be calculated 2 times
+ SongsWithVid: cardinal;
+ function FormatOverviewIntro(FormatStr: UTF8String): UTF8String;
+ function FormatSongOverview(FormatStr: UTF8String): UTF8String;
+ function FormatPlayerOverview(FormatStr: UTF8String): UTF8String;
+ public
+ TextOverview: integer;
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ procedure OnShow; override;
+ procedure SetAnimationProgress(Progress: real); override;
+
+ procedure SetOverview;
+ end;
+
+implementation
+
+uses
+ UGraphic,
+ UDataBase,
+ USongs,
+ USong,
+ ULanguage,
+ UCommon,
+ Classes,
+ ULog,
+ UUnicodeUtils;
+
+function TScreenStatMain.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if (PressedDown) then
+ begin // Key Down
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE :
+ begin
+ Ini.Save;
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenMain);
+ end;
+ SDLK_RETURN:
+ begin
+ //Exit Button Pressed
+ if Interaction = 4 then
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ FadeTo(@ScreenMain);
+ end
+ else //One of the Stats Buttons Pressed
+ begin
+ AudioPlayback.PlaySound(SoundLib.Back);
+ ScreenStatDetail.Typ := TStatType(Interaction);
+ FadeTo(@ScreenStatDetail);
+ end;
+ end;
+ SDLK_LEFT:
+ begin
+ InteractPrev;
+ end;
+ SDLK_RIGHT:
+ begin
+ InteractNext;
+ end;
+ SDLK_UP:
+ begin
+ InteractPrev;
+ end;
+ SDLK_DOWN:
+ begin
+ InteractNext;
+ end;
+ end;
+ end;
+end;
+
+constructor TScreenStatMain.Create;
+var
+ I: integer;
+begin
+ inherited Create;
+
+ TextOverview := AddText(Theme.StatMain.TextOverview);
+
+ LoadFromTheme(Theme.StatMain);
+
+ AddButton(Theme.StatMain.ButtonScores);
+ if (Length(Button[0].Text)=0) then
+ AddButtonText(14, 20, Theme.StatDetail.Description[0]);
+
+ AddButton(Theme.StatMain.ButtonSingers);
+ if (Length(Button[1].Text)=0) then
+ AddButtonText(14, 20, Theme.StatDetail.Description[1]);
+
+ AddButton(Theme.StatMain.ButtonSongs);
+ if (Length(Button[2].Text)=0) then
+ AddButtonText(14, 20, Theme.StatDetail.Description[2]);
+
+ AddButton(Theme.StatMain.ButtonBands);
+ if (Length(Button[3].Text)=0) then
+ AddButtonText(14, 20, Theme.StatDetail.Description[3]);
+
+ AddButton(Theme.StatMain.ButtonExit);
+ if (Length(Button[4].Text)=0) then
+ AddButtonText(14, 20, Theme.Options.Description[4]);
+
+ Interaction := 0;
+
+ //Set Songs with Vid
+ SongsWithVid := 0;
+ for I := 0 to Songs.SongList.Count -1 do
+ if (TSong(Songs.SongList[I]).Video.IsSet) then
+ Inc(SongsWithVid);
+end;
+
+procedure TScreenStatMain.OnShow;
+begin
+ inherited;
+
+ //Set Overview Text:
+ SetOverview;
+end;
+
+function TScreenStatMain.FormatOverviewIntro(FormatStr: UTF8String): UTF8String;
+var
+ Year, Month, Day: word;
+begin
+ {Format:
+ %0:d Ultrastar Version
+ %1:d Day of Reset
+ %2:d Month of Reset
+ %3:d Year of Reset}
+
+ Result := '';
+
+ try
+ DecodeDate(Database.GetStatReset(), Year, Month, Day);
+ Result := Format(FormatStr, [Language.Translate('US_VERSION'), Day, Month, Year]);
+ except
+ on E: EConvertError do
+ Log.LogError('Error Parsing FormatString "STAT_OVERVIEW_INTRO": ' + E.Message);
+ end;
+end;
+
+function TScreenStatMain.FormatSongOverview(FormatStr: UTF8String): UTF8String;
+var
+ CntSongs, CntSungSongs, CntVidSongs: integer;
+ MostPopSongArtist, MostPopSongTitle: UTF8String;
+ StatList: TList;
+ MostSungSong: TStatResultMostSungSong;
+begin
+ {Format:
+ %0:d Count Songs
+ %1:d Count of Sung Songs
+ %2:d Count of UnSung Songs
+ %3:d Count of Songs with Video
+ %4:s Name of the most popular Song}
+
+ CntSongs := Songs.SongList.Count;
+ CntSungSongs := Database.GetTotalEntrys(stMostSungSong);
+ CntVidSongs := SongsWithVid;
+
+ StatList := Database.GetStats(stMostSungSong, 1, 0, false);
+ if ((StatList <> nil) and (StatList.Count > 0)) then
+ begin
+ MostSungSong := StatList[0];
+ MostPopSongArtist := MostSungSong.Artist;
+ MostPopSongTitle := MostSungSong.Title;
+ end
+ else
+ begin
+ MostPopSongArtist := '-';
+ MostPopSongTitle := '-';
+ end;
+ Database.FreeStats(StatList);
+
+ Result := '';
+
+ try
+ Result := Format(FormatStr, [
+ CntSongs, CntSungSongs, CntSongs-CntSungSongs, CntVidSongs,
+ MostPopSongArtist, MostPopSongTitle]);
+ except
+ on E: EConvertError do
+ Log.LogError('Error Parsing FormatString "STAT_OVERVIEW_SONG": ' + E.Message);
+ end;
+end;
+
+function TScreenStatMain.FormatPlayerOverview(FormatStr: UTF8String): UTF8String;
+var
+ CntPlayers: integer;
+ BestScoreStat: TStatResultBestScores;
+ BestSingerStat: TStatResultBestSingers;
+ BestPlayer, BestScorePlayer: UTF8String;
+ BestPlayerScore, BestScore: integer;
+ SingerStats, ScoreStats: TList;
+begin
+ {Format:
+ %0:d Count Players
+ %1:s Best Player
+ %2:d Best Players Score
+ %3:s Best Score Player
+ %4:d Best Score}
+
+ CntPlayers := Database.GetTotalEntrys(stBestSingers);
+
+ SingerStats := Database.GetStats(stBestSingers, 1, 0, false);
+ if ((SingerStats <> nil) and (SingerStats.Count > 0)) then
+ begin
+ BestSingerStat := SingerStats[0];
+ BestPlayer := BestSingerStat.Player;
+ BestPlayerScore := BestSingerStat.AverageScore;
+ end
+ else
+ begin
+ BestPlayer := '-';
+ BestPlayerScore := 0;
+ end;
+ Database.FreeStats(SingerStats);
+
+ ScoreStats := Database.GetStats(stBestScores, 1, 0, false);
+ if ((ScoreStats <> nil) and (ScoreStats.Count > 0)) then
+ begin
+ BestScoreStat := ScoreStats[0];
+ BestScorePlayer := BestScoreStat.Singer;
+ BestScore := BestScoreStat.Score;
+ end
+ else
+ begin
+ BestScorePlayer := '-';
+ BestScore := 0;
+ end;
+ Database.FreeStats(ScoreStats);
+
+ Result := '';
+
+ try
+ Result := Format(Formatstr, [
+ CntPlayers, BestPlayer, BestPlayerScore,
+ BestScorePlayer, BestScore]);
+ except
+ on E: EConvertError do
+ Log.LogError('Error Parsing FormatString "STAT_OVERVIEW_PLAYER": ' + E.Message);
+ end;
+end;
+
+procedure TScreenStatMain.SetOverview;
+var
+ Overview: UTF8String;
+begin
+ // Format overview
+ Overview := FormatOverviewIntro(Language.Translate('STAT_OVERVIEW_INTRO')) + '\n \n' +
+ FormatSongOverview(Language.Translate('STAT_OVERVIEW_SONG')) + '\n \n' +
+ FormatPlayerOverview(Language.Translate('STAT_OVERVIEW_PLAYER'));
+ Text[0].Text := Overview;
+end;
+
+procedure TScreenStatMain.SetAnimationProgress(Progress: real);
+var
+ I: integer;
+begin
+ for I := 0 to high(Button) do
+ Button[I].Texture.ScaleW := Progress;
+end;
+
+end.
diff --git a/src/screens/UScreenTop5.pas b/src/screens/UScreenTop5.pas
new file mode 100644
index 00000000..75b9f160
--- /dev/null
+++ b/src/screens/UScreenTop5.pas
@@ -0,0 +1,307 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UScreenTop5;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SysUtils,
+ SDL,
+ UDisplay,
+ UMenu,
+ UMusic,
+ USongs,
+ UThemes;
+
+type
+ TScreenTop5 = class(TMenu)
+ public
+ TextLevel: integer;
+ TextArtistTitle: integer;
+ DifficultyShow: integer;
+
+ StaticNumber: array[1..5] of integer;
+ TextNumber: array[1..5] of integer;
+ TextName: array[1..5] of integer;
+ TextScore: array[1..5] of integer;
+ TextDate: array[1..5] of integer;
+
+ Fadeout: boolean;
+
+ constructor Create; override;
+ function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
+ function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; override;
+ procedure OnShow; override;
+ procedure DrawScores(difficulty: integer);
+ function Draw: boolean; override;
+ end;
+
+implementation
+
+uses
+ UDataBase,
+ UGraphic,
+ UMain,
+ UIni,
+ UNote,
+ UUnicodeUtils;
+
+function TScreenTop5.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
+begin
+ Result := true;
+ if PressedDown then
+ begin
+ // check normal keys
+ case UCS4UpperCase(CharCode) of
+ Ord('Q'):
+ begin
+ Result := false;
+ Exit;
+ end;
+ end;
+
+ // check special keys
+ case PressedKey of
+ SDLK_ESCAPE,
+ SDLK_BACKSPACE,
+ SDLK_RETURN:
+ begin
+ if (not Fadeout) then
+ begin
+ FadeTo(@ScreenSong);
+ Fadeout := true;
+ end;
+ end;
+ SDLK_RIGHT:
+ begin
+ inc(DifficultyShow);
+ if (DifficultyShow>2) then
+ DifficultyShow:=0;
+ DrawScores(DifficultyShow);
+ end;
+ SDLK_LEFT:
+ begin
+ dec(DifficultyShow);
+ if (DifficultyShow<0) then
+ DifficultyShow:=2;
+ DrawScores(DifficultyShow);
+ end;
+ SDLK_UP:
+ begin
+ inc(DifficultyShow);
+ if (DifficultyShow>2) then
+ DifficultyShow:=0;
+ DrawScores(DifficultyShow);
+ end;
+ SDLK_DOWN:
+ begin
+ dec(DifficultyShow);
+ if (DifficultyShow<0) then
+ DifficultyShow:=2;
+ DrawScores(DifficultyShow);
+ end;
+ SDLK_SYSREQ:
+ begin
+ Display.SaveScreenShot;
+ end;
+ end;
+ end;
+end;
+
+function TScreenTop5.ParseMouse(MouseButton: integer;
+ BtnDown: boolean;
+ X, Y: integer): boolean;
+begin
+ Result := true;
+ if (MouseButton = SDL_BUTTON_LEFT) and BtnDown then
+ //left-click anywhere sends return
+ ParseInput(SDLK_RETURN, 0, true);
+end;
+
+constructor TScreenTop5.Create;
+var
+ I: integer;
+begin
+ inherited Create;
+
+ LoadFromTheme(Theme.Top5);
+
+ TextLevel := AddText(Theme.Top5.TextLevel);
+ TextArtistTitle := AddText(Theme.Top5.TextArtistTitle);
+
+ for I := 0 to 4 do
+ begin
+ StaticNumber[I+1] := AddStatic(Theme.Top5.StaticNumber[I]);
+ TextNumber[I+1] := AddText (Theme.Top5.TextNumber[I]);
+ TextName[I+1] := AddText (Theme.Top5.TextName[I]);
+ TextScore[I+1] := AddText (Theme.Top5.TextScore[I]);
+ TextDate[I+1] := AddText (Theme.Top5.TextDate[I]);
+ end;
+
+end;
+
+procedure TScreenTop5.OnShow;
+var
+ I: integer;
+ PMax: integer;
+ sung: boolean; //score added? otherwise in wasn't sung!
+begin
+ inherited;
+
+ sung := false;
+ Fadeout := false;
+ DifficultyShow := Ini.Difficulty;
+
+ //ReadScore(CurrentSong);
+
+ PMax := Ini.Players;
+ if PMax = 4 then
+ PMax := 5;
+ for I := 0 to PMax do
+ begin
+ if (Round(Player[I].ScoreTotalInt) > 0) and (ScreenSing.SungToEnd) then
+ begin
+ DataBase.AddScore(CurrentSong, Ini.Difficulty, Ini.Name[I], Round(Player[I].ScoreTotalInt));
+ sung:=true;
+ end;
+ end;
+
+ if sung then
+ DataBase.WriteScore(CurrentSong);
+ DataBase.ReadScore(CurrentSong);
+
+ Text[TextArtistTitle].Text := CurrentSong.Artist + ' - ' + CurrentSong.Title;
+
+ for I := 1 to Length(CurrentSong.Score[Ini.Difficulty]) do
+ begin
+ Statics[StaticNumber[I]].Visible := true;
+ Text[TextNumber[I]].Visible := true;
+ Text[TextName[I]].Visible := true;
+ Text[TextScore[I]].Visible := true;
+ Text[TextDate[I]].Visible := true;
+
+ Text[TextName[I]].Text := CurrentSong.Score[Ini.Difficulty, I-1].Name;
+ Text[TextScore[I]].Text := IntToStr(CurrentSong.Score[Ini.Difficulty, I-1].Score);
+ Text[TextDate[I]].Text := CurrentSong.Score[Ini.Difficulty, I-1].Date;
+ end;
+
+ for I := Length(CurrentSong.Score[Ini.Difficulty]) + 1 to 5 do
+ begin
+ Statics[StaticNumber[I]].Visible := false;
+ Text[TextNumber[I]].Visible := false;
+ Text[TextName[I]].Visible := false;
+ Text[TextScore[I]].Visible := false;
+ Text[TextDate[I]].Visible := false;
+ end;
+
+ Text[TextLevel].Text := IDifficultyTranslated[Ini.Difficulty];
+end;
+
+procedure TScreenTop5.DrawScores(difficulty: integer);
+var
+ I: integer;
+begin
+ for I := 1 to Length(CurrentSong.Score[difficulty]) do
+ begin
+ Statics[StaticNumber[I]].Visible := true;
+ Text[TextNumber[I]].Visible := true;
+ Text[TextName[I]].Visible := true;
+ Text[TextScore[I]].Visible := true;
+ Text[TextDate[I]].Visible := true;
+
+ Text[TextName[I]].Text := CurrentSong.Score[difficulty, I-1].Name;
+ Text[TextScore[I]].Text := IntToStr(CurrentSong.Score[difficulty, I-1].Score);
+ Text[TextDate[I]].Text := CurrentSong.Score[difficulty, I-1].Date;
+ end;
+
+ for I := Length(CurrentSong.Score[difficulty]) + 1 to 5 do
+ begin
+ Statics[StaticNumber[I]].Visible := false;
+ Text[TextNumber[I]].Visible := false;
+ Text[TextName[I]].Visible := false;
+ Text[TextScore[I]].Visible := false;
+ Text[TextDate[I]].Visible := false;
+ end;
+
+ Text[TextLevel].Text := IDifficulty[difficulty];
+end;
+
+function TScreenTop5.Draw: boolean;
+//var
+{
+ Min: real;
+ Max: real;
+ Factor: real;
+ Factor2: real;
+
+ Item: integer;
+ P: integer;
+ C: integer;
+}
+begin
+ // Singstar - let it be...... with 6 statics
+(*
+ if PlayersPlay = 6 then
+ begin
+ for Item := 4 to 6 do
+ begin
+ if ScreenAct = 1 then P := Item-4;
+ if ScreenAct = 2 then P := Item-1;
+
+ FillPlayer(Item, P);
+{
+ if ScreenAct = 1 then
+ begin
+ LoadColor(
+ Statics[StaticBoxLightest[Item]].Texture.ColR,
+ Statics[StaticBoxLightest[Item]].Texture.ColG,
+ Statics[StaticBoxLightest[Item]].Texture.ColB,
+ 'P1Dark');
+ end;
+
+ if ScreenAct = 2 then
+ begin
+ LoadColor(
+ Statics[StaticBoxLightest[Item]].Texture.ColR,
+ Statics[StaticBoxLightest[Item]].Texture.ColG,
+ Statics[StaticBoxLightest[Item]].Texture.ColB,
+ 'P4Dark');
+ end;
+}
+ end;
+ end;
+*)
+
+ Result := inherited Draw;
+end;
+
+end.