- A won field pops up and fades into the color of the winning team
- At the end, the winning line pulses while all other fields are dimmed
- The jokers are labeled ("New song", "Replay a field"), so it's clear what they do
- Darker panels behind the team areas and more room between the board and the title
- Long player names no longer reach into the board
Co-authored-by: Florian Ostertag <flokuep@users.noreply.github.com>
501 lines
16 KiB
C#
501 lines
16 KiB
C#
#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 <http://www.gnu.org/licenses/>.
|
|
#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.TicTacToe
|
|
{
|
|
public class CRound
|
|
{
|
|
public int[] SongIds;
|
|
public int SingerTeam1;
|
|
public int SingerTeam2;
|
|
public int PointsTeam1;
|
|
public int PointsTeam2;
|
|
public int Winner;
|
|
public bool Finished;
|
|
}
|
|
|
|
public abstract class CPartyScreenTicTacToe : CMenuParty
|
|
{
|
|
protected new CPartyModeTicTacToe _PartyMode;
|
|
|
|
public override void Init()
|
|
{
|
|
base.Init();
|
|
_PartyMode = (CPartyModeTicTacToe)base._PartyMode;
|
|
}
|
|
}
|
|
|
|
// ReSharper disable ClassNeverInstantiated.Global
|
|
public sealed class CPartyModeTicTacToe : CPartyMode
|
|
// ReSharper restore ClassNeverInstantiated.Global
|
|
{
|
|
public override int MinMics
|
|
{
|
|
get { return 2; }
|
|
}
|
|
public override int MaxMics
|
|
{
|
|
get { return 2; }
|
|
}
|
|
public override int MinPlayers
|
|
{
|
|
get { return 2; }
|
|
}
|
|
public override int MaxPlayers
|
|
{
|
|
get { return 20; }
|
|
}
|
|
public override int MinTeams
|
|
{
|
|
get { return 2; }
|
|
}
|
|
public override int MaxTeams
|
|
{
|
|
get { return 2; }
|
|
}
|
|
|
|
public override int MinPlayersPerTeam
|
|
{
|
|
get { return 1; }
|
|
}
|
|
public override int MaxPlayersPerTeam
|
|
{
|
|
get { return 10; }
|
|
}
|
|
|
|
public readonly EGameMode[] AvailableGameModes = { EGameMode.TR_GAMEMODE_NORMAL, EGameMode.TR_GAMEMODE_SHORTSONG, EGameMode.TR_GAMEMODE_DUET };
|
|
|
|
private enum EStage
|
|
{
|
|
Config,
|
|
Songs,
|
|
Names,
|
|
Main,
|
|
Singing
|
|
}
|
|
|
|
public struct SData
|
|
{
|
|
public int NumPlayerTeam1;
|
|
public int NumPlayerTeam2;
|
|
public int NumFields;
|
|
public int NumJockers;
|
|
public int Team;
|
|
public List<Guid> ProfileIdsTeam1;
|
|
public List<Guid> ProfileIdsTeam2;
|
|
|
|
public ESongSource SongSource;
|
|
public ESongSorting Sorting;
|
|
public int CategoryIndex;
|
|
public int PlaylistId;
|
|
|
|
public EGameMode GameMode;
|
|
public int NumMedleySongs;
|
|
|
|
public List<CRound> Rounds;
|
|
public List<int> Songs;
|
|
|
|
public int CurrentRoundNr;
|
|
public int FieldNr;
|
|
public int LastWonField;
|
|
|
|
public int[] NumJokerRandom;
|
|
public int[] NumJokerRetry;
|
|
public EOffOn RefillJokers;
|
|
public EOffOn SwitchPlayer;
|
|
}
|
|
|
|
public SData GameData;
|
|
private EStage _Stage;
|
|
|
|
public CPartyModeTicTacToe(int id) : base(id)
|
|
{
|
|
_ScreenSongOptions.Selection.RandomOnly = false;
|
|
_ScreenSongOptions.Selection.PartyMode = true;
|
|
_ScreenSongOptions.Selection.CategoryChangeAllowed = true;
|
|
_ScreenSongOptions.Selection.NumJokers = new int[] { 5, 5 };
|
|
_ScreenSongOptions.Selection.TeamNames = new string[] { "foo", "bar" };
|
|
|
|
_ScreenSongOptions.Sorting.SearchString = string.Empty;
|
|
_ScreenSongOptions.Sorting.SearchActive = false;
|
|
_ScreenSongOptions.Sorting.DuetOptions = EDuetOptions.NoDuets;
|
|
|
|
GameData = new SData
|
|
{
|
|
NumPlayerTeam1 = 2,
|
|
NumPlayerTeam2 = 2,
|
|
NumFields = 9,
|
|
NumJockers = 1,
|
|
ProfileIdsTeam1 = new List<Guid>(),
|
|
ProfileIdsTeam2 = new List<Guid>(),
|
|
CurrentRoundNr = 0,
|
|
FieldNr = 0,
|
|
Sorting = CBase.Config.GetSongSorting(),
|
|
SongSource = ESongSource.TR_SONGSOURCE_ALLSONGS,
|
|
PlaylistId = 0,
|
|
CategoryIndex = 0,
|
|
GameMode = EGameMode.TR_GAMEMODE_NORMAL,
|
|
NumMedleySongs = 5,
|
|
Rounds = new List<CRound>(),
|
|
Songs = new List<int>(),
|
|
NumJokerRandom = new int[2],
|
|
NumJokerRetry = new int[2],
|
|
RefillJokers = EOffOn.TR_CONFIG_OFF
|
|
};
|
|
}
|
|
|
|
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;
|
|
|
|
if (CBase.Config.GetTabs() == EOffOn.TR_CONFIG_ON && _ScreenSongOptions.Sorting.SongSorting != ESongSorting.TR_CONFIG_NONE)
|
|
{
|
|
_ScreenSongOptions.Sorting.Tabs = EOffOn.TR_CONFIG_ON;
|
|
}
|
|
|
|
GameData.Songs.Clear();
|
|
GameData.Rounds.Clear();
|
|
}
|
|
|
|
public override bool Init()
|
|
{
|
|
if (!base.Init())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SetDefaults();
|
|
return true;
|
|
}
|
|
|
|
public override void UpdateGame()
|
|
{
|
|
/*
|
|
if (CBase.Songs.IsInCategory() || _ScreenSongOptions.Sorting.Tabs == EOffOn.TR_CONFIG_OFF)
|
|
_ScreenSongOptions.Selection.RandomOnly = true;
|
|
else
|
|
_ScreenSongOptions.Selection.RandomOnly = false;*/
|
|
}
|
|
|
|
private IMenu _GetNextScreen()
|
|
{
|
|
switch (_Stage)
|
|
{
|
|
case EStage.Config:
|
|
return _Screens["CPartyScreenTicTacToeConfig"];
|
|
case EStage.Songs:
|
|
return _Screens["CPartyScreenTicTacToeSongs"];
|
|
case EStage.Names:
|
|
return _Screens["CPartyScreenTicTacToeNames"];
|
|
case EStage.Main:
|
|
return _Screens["CPartyScreenTicTacToeMain"];
|
|
case EStage.Singing:
|
|
return CBase.Graphics.GetScreen(EScreen.Sing);
|
|
default:
|
|
throw new ArgumentException("Invalid stage: " + _Stage);
|
|
}
|
|
}
|
|
|
|
private void _FadeToScreen()
|
|
{
|
|
if (CBase.Graphics.GetNextScreen() != _GetNextScreen())
|
|
{
|
|
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;
|
|
GameData.Team = CBase.Game.GetRandom(100) < 50 ? 0 : 1;
|
|
CBase.Songs.ResetSongSung();
|
|
GameData.CurrentRoundNr = 1;
|
|
GameData.LastWonField = -1;
|
|
_CreateRounds();
|
|
_SetNumJokers();
|
|
break;
|
|
case EStage.Main:
|
|
_Stage = EStage.Singing;
|
|
_StartRound(GameData.FieldNr);
|
|
break;
|
|
case EStage.Singing:
|
|
_Stage = EStage.Main;
|
|
GameData.Team = GameData.Team == 1 ? 0 : 1;
|
|
if (GameData.RefillJokers == EOffOn.TR_CONFIG_ON)
|
|
{
|
|
_SetNumJokers();
|
|
}
|
|
|
|
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: // Rest is not allowed
|
|
throw new ArgumentException("Invalid stage: " + _Stage);
|
|
}
|
|
|
|
_FadeToScreen();
|
|
}
|
|
|
|
public override IMenu GetStartScreen()
|
|
{
|
|
return _Screens["CPartyScreenTicTacToeConfig"];
|
|
}
|
|
|
|
public override SScreenSongOptions GetScreenSongOptions()
|
|
{
|
|
throw new ArgumentException("Not required!");
|
|
}
|
|
|
|
public override void OnSongChange(int songIndex, ref SScreenSongOptions screenSongOptions)
|
|
{
|
|
throw new ArgumentException("Not required!");
|
|
}
|
|
|
|
public override void OnCategoryChange(int categoryIndex, ref SScreenSongOptions screenSongOptions)
|
|
{
|
|
throw new ArgumentException("Not required!");
|
|
}
|
|
|
|
public override void SetSearchString(string searchString, bool visible)
|
|
{
|
|
throw new ArgumentException("Not required!");
|
|
}
|
|
|
|
public override void JokerUsed(int teamNr) { }
|
|
|
|
public override void SongSelected(int songId)
|
|
{
|
|
throw new ArgumentException("Not required!");
|
|
}
|
|
|
|
public override void LeavingHighscore()
|
|
{
|
|
CBase.Songs.AddPartySongSung(CBase.Game.GetSong(0).Id);
|
|
_UpdateScores();
|
|
Next();
|
|
}
|
|
|
|
private void _CreateRounds()
|
|
{
|
|
GameData.Rounds = new List<CRound>();
|
|
for (var i = 0; i < GameData.NumFields; i++)
|
|
{
|
|
var r = new CRound();
|
|
GameData.Rounds.Add(r);
|
|
}
|
|
}
|
|
|
|
public 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(GameData.GameMode))
|
|
{
|
|
GameData.Songs.Add(id);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ESongSource.TR_SONGSOURCE_ALLSONGS:
|
|
var avSongs = CBase.Songs.GetSongs();
|
|
GameData.Songs.AddRange(avSongs.Where(song => song.IsGameModeAvailable(GameData.GameMode)).Select(song => song.Id));
|
|
break;
|
|
|
|
case ESongSource.TR_SONGSOURCE_CATEGORY:
|
|
CBase.Songs.SortSongs(GameData.Sorting, EOffOn.TR_CONFIG_ON, CBase.Config.GetIgnoreArticles(), "", EDuetOptions.All, -1);
|
|
CBase.Songs.SetCategory(GameData.CategoryIndex);
|
|
avSongs = CBase.Songs.GetVisibleSongs();
|
|
GameData.Songs.AddRange(avSongs.Where(song => song.IsGameModeAvailable(GameData.GameMode)).Select(song => song.Id));
|
|
|
|
CBase.Songs.SetCategory(-1);
|
|
break;
|
|
}
|
|
|
|
GameData.Songs.Shuffle();
|
|
}
|
|
|
|
private void _StartRound(int roundNr)
|
|
{
|
|
CBase.Game.Reset();
|
|
CBase.Game.ClearSongs();
|
|
|
|
CBase.Game.SetNumPlayer(2);
|
|
|
|
var players = CBase.Game.GetPlayers();
|
|
if (players == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (players.Length < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var round = GameData.Rounds[roundNr];
|
|
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
//default values
|
|
players[i].ProfileId = Guid.Empty;
|
|
}
|
|
|
|
//try to fill with the right data
|
|
players[0].ProfileId = GameData.ProfileIdsTeam1[round.SingerTeam1];
|
|
players[1].ProfileId = GameData.ProfileIdsTeam2[round.SingerTeam2];
|
|
|
|
foreach (var song in round.SongIds)
|
|
{
|
|
CBase.Game.AddSong(song, GameData.GameMode);
|
|
}
|
|
}
|
|
|
|
private void _SetNumJokers()
|
|
{
|
|
GameData.NumJokerRandom[0] = GameData.NumJockers;
|
|
GameData.NumJokerRandom[1] = GameData.NumJockers;
|
|
|
|
switch (GameData.NumFields)
|
|
{
|
|
case 9:
|
|
GameData.NumJokerRetry[0] = 0;
|
|
GameData.NumJokerRetry[1] = 0;
|
|
break;
|
|
|
|
case 16:
|
|
GameData.NumJokerRetry[0] = 1;
|
|
GameData.NumJokerRetry[1] = 1;
|
|
break;
|
|
|
|
case 25:
|
|
GameData.NumJokerRetry[0] = 2;
|
|
GameData.NumJokerRetry[1] = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void _UpdateScores()
|
|
{
|
|
if (!GameData.Rounds[GameData.FieldNr].Finished)
|
|
{
|
|
GameData.CurrentRoundNr++;
|
|
}
|
|
|
|
var points = CBase.Game.GetPoints();
|
|
var results = CBase.Game.GetPlayers();
|
|
|
|
if (results == null || results.Length < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Sum up all points
|
|
for (var r = 0; r < points.NumRounds; r++)
|
|
{
|
|
var player = points.GetPlayer(r, 2);
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
results[i].Points += player[i].Points;
|
|
results[i].PointsGoldenNotes += player[i].PointsGoldenNotes;
|
|
results[i].PointsLineBonus += player[i].PointsLineBonus;
|
|
}
|
|
}
|
|
|
|
//Calc average of points
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
results[i].Points /= points.NumRounds;
|
|
results[i].PointsGoldenNotes /= points.NumRounds;
|
|
results[i].PointsLineBonus /= points.NumRounds;
|
|
}
|
|
|
|
//Decide who has won
|
|
GameData.Rounds[GameData.FieldNr].PointsTeam1 = (int)Math.Round(results[0].Points);
|
|
GameData.Rounds[GameData.FieldNr].PointsTeam2 = (int)Math.Round(results[1].Points);
|
|
GameData.Rounds[GameData.FieldNr].Finished = true;
|
|
GameData.LastWonField = GameData.FieldNr;
|
|
if (GameData.Rounds[GameData.FieldNr].PointsTeam1 < GameData.Rounds[GameData.FieldNr].PointsTeam2)
|
|
{
|
|
GameData.Rounds[GameData.FieldNr].Winner = 2;
|
|
}
|
|
else if (GameData.Rounds[GameData.FieldNr].PointsTeam1 > GameData.Rounds[GameData.FieldNr].PointsTeam2)
|
|
{
|
|
GameData.Rounds[GameData.FieldNr].Winner = 1;
|
|
}
|
|
else
|
|
{
|
|
GameData.Rounds[GameData.FieldNr].Finished = false;
|
|
GameData.LastWonField = -1;
|
|
GameData.CurrentRoundNr--;
|
|
}
|
|
}
|
|
}
|
|
} |