#region license
// This file is part of Vocaluxe.
//
// Vocaluxe 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 3 of the License, or
// (at your option) any later version.
//
// Vocaluxe 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 Vocaluxe. If not, see .
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using VocaluxeLib.Menu;
using VocaluxeLib.Utils;
[assembly: ComVisible(false)]
namespace VocaluxeLib.PartyModes.Tournament
{
public class CTournamentMatch
{
public int Round;
public int Index;
public int Player1 = -1;
public int Player2 = -1;
public int Score1;
public int Score2;
public int Winner = -1;
public bool Bye;
public bool Played
{
get { return Winner >= 0; }
}
public bool Ready
{
get { return !Played && Player1 >= 0 && Player2 >= 0; }
}
public int WinnerPlayer
{
get
{
if (Winner == 0)
{
return Player1;
}
return Winner == 1 ? Player2 : -1;
}
}
public int LoserPlayer
{
get
{
if (Winner == 0)
{
return Player2;
}
return Winner == 1 ? Player1 : -1;
}
}
}
public abstract class CPartyScreenTournament : CMenuParty
{
protected new CPartyModeTournament _PartyMode;
public override void Init()
{
base.Init();
_PartyMode = (CPartyModeTournament)base._PartyMode;
}
}
public sealed class CPartyModeTournament : CPartyMode
{
public const int NumPlayerAtOnce = 2;
public const int MaxJokers = 3;
public override int MinMics
{
get { return NumPlayerAtOnce; }
}
public override int MaxMics
{
get { return CBase.Config.GetMaxNumMics(); }
}
public override int MinPlayers
{
get { return 3; }
}
public override int MaxPlayers
{
get { return 16; }
}
public override int MinTeams
{
get { return 1; }
}
public override int MaxTeams
{
get { return 1; }
}
public override int MinPlayersPerTeam
{
get { return MinPlayers; }
}
public override int MaxPlayersPerTeam
{
get { return MaxPlayers; }
}
private enum EStage
{
Config,
Songs,
Names,
Main,
SongSelection,
Singing,
MedleySinging
}
public struct SData
{
public int NumPlayer;
public int NumJokers;
public int[] Jokers;
public List ProfileIds;
public ESongSource SongSource;
public ESongSorting Sorting;
public int CategoryIndex;
public int PlaylistId;
public EGameMode GameMode;
public int NumMedleySongs;
public List Rounds;
public bool IsRematch;
public int NumMatchesPlayed;
public CTournamentMatch LastDecidedMatch;
public List Songs;
}
public SData GameData;
private EStage _Stage;
public CPartyModeTournament(int id) : base(id)
{
_ScreenSongOptions.Selection.RandomOnly = false;
_ScreenSongOptions.Selection.PartyMode = true;
_ScreenSongOptions.Selection.CategoryChangeAllowed = true;
_ScreenSongOptions.Selection.NumJokers = new int[] { 1, 1 };
_ScreenSongOptions.Selection.TeamNames = new string[] { "foo", "bar" };
_ScreenSongOptions.Sorting.SearchString = string.Empty;
_ScreenSongOptions.Sorting.SearchActive = false;
_ScreenSongOptions.Sorting.DuetOptions = EDuetOptions.NoDuets;
_ScreenSongOptions.Sorting.FilterPlaylistId = -1;
GameData = new SData
{
NumPlayer = 8,
NumJokers = 1,
ProfileIds = new List(),
Sorting = CBase.Config.GetSongSorting(),
SongSource = ESongSource.TR_SONGSOURCE_ALLSONGS,
PlaylistId = 0,
CategoryIndex = 0,
GameMode = EGameMode.TR_GAMEMODE_NORMAL,
NumMedleySongs = 5,
Rounds = new List(),
Songs = new List()
};
}
public override void SetDefaults()
{
_Stage = EStage.Config;
_ScreenSongOptions.Sorting.IgnoreArticles = CBase.Config.GetIgnoreArticles();
_ScreenSongOptions.Sorting.SongSorting = CBase.Config.GetSongSorting();
_ScreenSongOptions.Sorting.Tabs = EOffOn.TR_CONFIG_OFF;
_ScreenSongOptions.Selection.SongIndex = -1;
_ScreenSongOptions.Selection.CategoryIndex = -1;
if (CBase.Config.GetTabs() == EOffOn.TR_CONFIG_ON && _ScreenSongOptions.Sorting.SongSorting != ESongSorting.TR_CONFIG_NONE)
{
_ScreenSongOptions.Sorting.Tabs = EOffOn.TR_CONFIG_ON;
}
GameData.Rounds = new List();
}
public override bool Init()
{
if (!base.Init())
{
return false;
}
SetDefaults();
return true;
}
public override void UpdateGame() { }
public int NumRounds
{
get { return GameData.Rounds.Count; }
}
public CTournamentMatch FinalMatch
{
get { return GameData.Rounds.Count > 0 ? GameData.Rounds[GameData.Rounds.Count - 1][0] : null; }
}
public bool IsFinished
{
get { return FinalMatch != null && FinalMatch.Played; }
}
public CTournamentMatch CurrentMatch
{
get
{
foreach (var round in GameData.Rounds)
{
foreach (var match in round)
{
if (match.Ready)
{
return match;
}
}
}
return null;
}
}
private IMenu _GetNextScreen()
{
switch (_Stage)
{
case EStage.Config:
return _Screens["CPartyScreenTournamentConfig"];
case EStage.Songs:
return _Screens["CPartyScreenTournamentSongs"];
case EStage.Names:
return _Screens["CPartyScreenTournamentNames"];
case EStage.Main:
return _Screens["CPartyScreenTournamentMain"];
case EStage.SongSelection:
return CBase.Graphics.GetScreen(EScreen.Song);
case EStage.Singing:
case EStage.MedleySinging:
return CBase.Graphics.GetScreen(EScreen.Sing);
default:
throw new ArgumentException("Invalid stage: " + _Stage);
}
}
private void _FadeToScreen()
{
CBase.Graphics.FadeTo(_GetNextScreen());
}
public void Next()
{
switch (_Stage)
{
case EStage.Config:
_Stage = EStage.Songs;
break;
case EStage.Songs:
_Stage = EStage.Names;
break;
case EStage.Names:
_Stage = EStage.Main;
CBase.Songs.ResetSongSung();
_CreateBracket();
break;
case EStage.Main:
if (CurrentMatch == null)
{
return;
}
if (GameData.GameMode == EGameMode.TR_GAMEMODE_MEDLEY)
{
_Stage = EStage.MedleySinging;
_PrepareMedleyRound();
}
else
{
_Stage = EStage.SongSelection;
_PrepareSongSelection();
}
break;
case EStage.SongSelection:
_Stage = EStage.Singing;
break;
case EStage.MedleySinging:
case EStage.Singing:
_Stage = EStage.Main;
_UpdateScores();
break;
default:
throw new ArgumentException("Invalid stage: " + _Stage);
}
_FadeToScreen();
}
public void Back()
{
switch (_Stage)
{
case EStage.Config:
CBase.Graphics.FadeTo(EScreen.Party);
return;
case EStage.Songs:
_Stage = EStage.Config;
break;
case EStage.Names:
_Stage = EStage.Songs;
break;
case EStage.Main:
_Stage = EStage.Names;
break;
default:
throw new ArgumentException("Invalid stage: " + _Stage);
}
_FadeToScreen();
}
public override IMenu GetStartScreen()
{
return _Screens["CPartyScreenTournamentConfig"];
}
public override SScreenSongOptions GetScreenSongOptions()
{
return _ScreenSongOptions;
}
public override void OnSongChange(int songIndex, ref SScreenSongOptions screenSongOptions)
{
if (_ScreenSongOptions.Selection.SelectNextRandomSong && songIndex != -1)
{
_ScreenSongOptions.Selection.SelectNextRandomSong = false;
}
_ScreenSongOptions.Selection.SongIndex = songIndex;
screenSongOptions = _ScreenSongOptions;
}
public override void OnCategoryChange(int categoryIndex, ref SScreenSongOptions screenSongOptions)
{
if (categoryIndex != -1 || CBase.Config.GetTabs() == EOffOn.TR_CONFIG_OFF)
{
_ScreenSongOptions.Selection.SelectNextRandomSong = true;
_ScreenSongOptions.Selection.RandomOnly = true;
}
else
{
_ScreenSongOptions.Selection.SongIndex = -1;
_ScreenSongOptions.Selection.RandomOnly = false;
}
_ScreenSongOptions.Selection.CategoryIndex = categoryIndex;
screenSongOptions = _ScreenSongOptions;
}
public override void SetSearchString(string searchString, bool visible)
{
_ScreenSongOptions.Sorting.SearchString = searchString;
_ScreenSongOptions.Sorting.SearchActive = visible;
}
public override void JokerUsed(int teamNr)
{
if (_ScreenSongOptions.Selection.NumJokers == null || teamNr >= _ScreenSongOptions.Selection.NumJokers.Length)
{
return;
}
var match = CurrentMatch;
if (match != null && GameData.Jokers != null)
{
var player = teamNr == 0 ? match.Player1 : match.Player2;
if (GameData.Jokers[player] > 0)
{
GameData.Jokers[player]--;
}
}
_ScreenSongOptions.Selection.NumJokers[teamNr]--;
_ScreenSongOptions.Selection.RandomOnly = true;
_ScreenSongOptions.Selection.CategoryChangeAllowed = false;
}
public override void SongSelected(int songId)
{
_PrepareRound(new int[] { songId });
Next();
}
public override void LeavingHighscore()
{
for (var i = 0; i < CBase.Game.GetNumSongs(); i++)
{
CBase.Songs.AddPartySongSung(CBase.Game.GetSong(i).Id);
}
Next();
}
private void _CreateBracket()
{
var size = 4;
while (size < GameData.NumPlayer)
{
size *= 2;
}
GameData.Rounds = new List();
for (var count = size / 2; count >= 1; count /= 2)
{
var round = new CTournamentMatch[count];
for (var i = 0; i < count; i++)
{
round[i] = new CTournamentMatch { Round = GameData.Rounds.Count, Index = i };
}
GameData.Rounds.Add(round);
}
var firstRound = GameData.Rounds[0];
var half = firstRound.Length / 2;
var byeOrder = new List();
for (var i = 0; i < half; i++)
{
byeOrder.Add(i);
byeOrder.Add(half + i);
}
var numByes = size - GameData.NumPlayer;
for (var i = 0; i < numByes; i++)
{
firstRound[byeOrder[i]].Bye = true;
}
var players = Enumerable.Range(0, GameData.NumPlayer).ToList();
players.Shuffle();
var next = 0;
foreach (var match in firstRound)
{
match.Player1 = players[next++];
if (!match.Bye)
{
match.Player2 = players[next++];
}
}
foreach (var match in firstRound)
{
if (match.Bye)
{
match.Winner = 0;
_Advance(match);
}
}
GameData.Jokers = new int[GameData.NumPlayer];
for (var i = 0; i < GameData.NumPlayer; i++)
{
GameData.Jokers[i] = GameData.NumJokers;
}
GameData.IsRematch = false;
GameData.NumMatchesPlayed = 0;
GameData.LastDecidedMatch = null;
GameData.Songs = new List();
}
private void _Advance(CTournamentMatch match)
{
if (match.Round + 1 >= GameData.Rounds.Count)
{
return;
}
var nextMatch = GameData.Rounds[match.Round + 1][match.Index / 2];
if (match.Index % 2 == 0)
{
nextMatch.Player1 = match.WinnerPlayer;
}
else
{
nextMatch.Player2 = match.WinnerPlayer;
}
}
private void _PrepareMedleyRound()
{
var songIds = new List();
for (var i = 0; i < GameData.NumMedleySongs; i++)
{
if (GameData.Songs.Count == 0)
{
_UpdateSongList();
}
if (GameData.Songs.Count == 0)
{
break;
}
songIds.Add(GameData.Songs[0]);
GameData.Songs.RemoveAt(0);
}
_PrepareRound(songIds.ToArray());
}
private void _PrepareSongSelection()
{
_ScreenSongOptions.Selection.RandomOnly = true;
_ScreenSongOptions.Selection.SelectNextRandomSong = true;
_ScreenSongOptions.Sorting.IgnoreArticles = CBase.Config.GetIgnoreArticles();
switch (GameData.SongSource)
{
case ESongSource.TR_SONGSOURCE_ALLSONGS:
_ScreenSongOptions.Sorting.SongSorting = CBase.Config.GetSongSorting();
_ScreenSongOptions.Sorting.Tabs = CBase.Config.GetTabs();
_ScreenSongOptions.Sorting.FilterPlaylistId = -1;
_ScreenSongOptions.Selection.SongIndex = -1;
_ScreenSongOptions.Selection.CategoryIndex = -1;
_ScreenSongOptions.Selection.CategoryChangeAllowed = true;
break;
case ESongSource.TR_SONGSOURCE_CATEGORY:
_ScreenSongOptions.Sorting.SongSorting = GameData.Sorting;
_ScreenSongOptions.Sorting.Tabs = EOffOn.TR_CONFIG_ON;
_ScreenSongOptions.Sorting.FilterPlaylistId = -1;
_ScreenSongOptions.Selection.CategoryIndex = GameData.CategoryIndex;
_ScreenSongOptions.Selection.CategoryChangeAllowed = false;
break;
case ESongSource.TR_SONGSOURCE_PLAYLIST:
_ScreenSongOptions.Sorting.SongSorting = CBase.Config.GetSongSorting();
_ScreenSongOptions.Sorting.Tabs = EOffOn.TR_CONFIG_OFF;
_ScreenSongOptions.Sorting.FilterPlaylistId = GameData.PlaylistId;
_ScreenSongOptions.Selection.CategoryChangeAllowed = false;
break;
}
var match = CurrentMatch;
_ScreenSongOptions.Selection.NumJokers = new int[] { GameData.Jokers[match.Player1], GameData.Jokers[match.Player2] };
_ScreenSongOptions.Selection.TeamNames = new string[]
{
CBase.Profiles.GetPlayerName(GameData.ProfileIds[match.Player1]),
CBase.Profiles.GetPlayerName(GameData.ProfileIds[match.Player2])
};
}
private bool _PrepareRound(int[] songIds)
{
CBase.Game.Reset();
CBase.Game.ClearSongs();
CBase.Game.SetNumPlayer(NumPlayerAtOnce);
var players = CBase.Game.GetPlayers();
var match = CurrentMatch;
if (players == null || players.Length < NumPlayerAtOnce || match == null)
{
return false;
}
players[0].ProfileId = GameData.ProfileIds[match.Player1];
players[1].ProfileId = GameData.ProfileIds[match.Player2];
foreach (var songId in songIds)
{
var gameMode = GameData.GameMode;
if (GameData.IsRematch && gameMode != EGameMode.TR_GAMEMODE_MEDLEY && CBase.Songs.GetSongById(songId).IsGameModeAvailable(EGameMode.TR_GAMEMODE_SHORTSONG))
{
gameMode = EGameMode.TR_GAMEMODE_SHORTSONG;
}
CBase.Game.AddSong(songId, gameMode);
}
return true;
}
private void _UpdateSongList()
{
if (GameData.Songs.Count > 0)
{
return;
}
switch (GameData.SongSource)
{
case ESongSource.TR_SONGSOURCE_PLAYLIST:
for (var i = 0; i < CBase.Playlist.GetSongCount(GameData.PlaylistId); i++)
{
var id = CBase.Playlist.GetSong(GameData.PlaylistId, i).SongId;
if (CBase.Songs.GetSongById(id).IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY))
{
GameData.Songs.Add(id);
}
}
break;
case ESongSource.TR_SONGSOURCE_ALLSONGS:
var avSongs = CBase.Songs.GetSongs();
GameData.Songs.AddRange(avSongs.Where(song => song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY)).Select(song => song.Id));
break;
case ESongSource.TR_SONGSOURCE_CATEGORY:
var oldSorting = CBase.Config.GetSongSorting();
CBase.Songs.SortSongs(GameData.Sorting, EOffOn.TR_CONFIG_ON, CBase.Config.GetIgnoreArticles(), string.Empty, EDuetOptions.NoDuets, -1);
CBase.Songs.SetCategory(GameData.CategoryIndex);
avSongs = CBase.Songs.GetVisibleSongs();
GameData.Songs.AddRange(avSongs.Where(song => song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY)).Select(song => song.Id));
CBase.Songs.SetCategory(-1);
CBase.Songs.SortSongs(oldSorting, CBase.Config.GetTabs(), CBase.Config.GetIgnoreArticles(), string.Empty, EDuetOptions.NoDuets, -1);
break;
}
GameData.Songs.Shuffle();
}
private void _UpdateScores()
{
var match = CurrentMatch;
if (match == null)
{
return;
}
var points = CBase.Game.GetPoints();
if (points == null || points.NumRounds == 0)
{
return;
}
var scores = new double[NumPlayerAtOnce];
for (var round = 0; round < points.NumRounds; round++)
{
var res = points.GetPlayer(round, NumPlayerAtOnce);
if (res == null || res.Length < NumPlayerAtOnce)
{
return;
}
for (var p = 0; p < NumPlayerAtOnce; p++)
{
scores[p] += res[p].Points;
}
}
match.Score1 = (int)Math.Round(scores[0] / points.NumRounds);
match.Score2 = (int)Math.Round(scores[1] / points.NumRounds);
GameData.NumMatchesPlayed++;
if (match.Score1 == match.Score2)
{
GameData.IsRematch = true;
return;
}
GameData.IsRematch = false;
GameData.LastDecidedMatch = match;
match.Winner = match.Score1 > match.Score2 ? 0 : 1;
_Advance(match);
}
}
}