#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.Songs;
using VocaluxeLib.Utils;
[assembly: ComVisible(false)]
namespace VocaluxeLib.PartyModes.RuleRoulette
{
public enum ERule
{
ShortSong,
Medley,
DoublePack,
DuetOnly,
FreeChoice,
UnderdogChoice,
GoldRush,
PerfectLines,
TargetScore,
Reverse,
Teamwork,
DoubleWin,
DoublePoints,
Blitz,
AllOrNothing,
PointSteal,
LeadCounts,
Underdog,
JokerRain,
NoNet
}
public enum ERuleState
{
Random,
Always,
Never
}
public class CRoulettePlayer
{
public Guid ProfileId;
public int TablePoints;
public int SingPoints;
public int Duels;
public int Wins;
public int Jokers;
}
public class CRouletteDuel
{
public CRoulettePlayer[] Players = new CRoulettePlayer[2];
public ERule Rule;
public int TargetScore;
public CRoulettePlayer Chooser;
public int[] Scores = new int[2];
public int[] Awarded = new int[2];
public bool Decided;
}
public abstract class CPartyScreenRuleRoulette : CMenuParty
{
protected new CPartyModeRuleRoulette _PartyMode;
public override void Init()
{
base.Init();
_PartyMode = (CPartyModeRuleRoulette)base._PartyMode;
}
}
public sealed class CPartyModeRuleRoulette : CPartyMode
{
public const int PlayersPerDuel = 2;
public const int MaxJokers = 3;
public const int MinRulesOnWheel = 6;
public const int MaxRulesOnWheel = 12;
public const int MaxSongsPerPlayer = 10;
public const int MinSongsForRule = 5;
public const int MedleySongs = 3;
public const int TeamworkScore = 12000;
public const int DoubleWinScore = 7000;
public override int MinMics
{
get { return PlayersPerDuel; }
}
public override int MaxMics
{
get { return CBase.Config.GetMaxNumMics(); }
}
public override int MinPlayers
{
get { return 2; }
}
public override int MaxPlayers
{
get { return 12; }
}
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,
Rules,
Songs,
Names,
Main,
SongSelection,
Singing,
DirectSinging
}
public struct SData
{
public int NumPlayer;
public int SongsPerPlayer;
public int NumJokers;
public bool ManualRules;
public int NumRulesOnWheel;
public Dictionary RuleStates;
public List ProfileIds;
public ESongSource SongSource;
public ESongSorting Sorting;
public int CategoryIndex;
public int PlaylistId;
public EGameMode GameMode;
public int NumMedleySongs;
public List Players;
public List Duels;
public int DuelIndex;
public List Wheel;
public List UsedInCycle;
public bool RuleChosen;
public CRouletteDuel LastDuel;
public int NumDuelsPlayed;
}
public SData GameData;
private EStage _Stage;
private List _SourceSongs = new List();
private readonly HashSet _SungSongs = new HashSet();
public CPartyModeRuleRoulette(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.All;
_ScreenSongOptions.Sorting.FilterPlaylistId = -1;
var states = new Dictionary();
foreach (ERule rule in Enum.GetValues(typeof(ERule)))
{
states[rule] = ERuleState.Random;
}
GameData = new SData
{
NumPlayer = 4,
SongsPerPlayer = 3,
NumJokers = 1,
ManualRules = false,
NumRulesOnWheel = 8,
RuleStates = states,
ProfileIds = new List(),
Sorting = CBase.Config.GetSongSorting(),
SongSource = ESongSource.TR_SONGSOURCE_ALLSONGS,
PlaylistId = 0,
CategoryIndex = 0,
GameMode = EGameMode.TR_GAMEMODE_NORMAL,
NumMedleySongs = MedleySongs,
Players = new List(),
Duels = new List(),
Wheel = new List(),
UsedInCycle = 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;
}
}
public override bool Init()
{
if (!base.Init())
{
return false;
}
SetDefaults();
return true;
}
public override void UpdateGame() { }
public bool IsFinished
{
get { return GameData.Duels.Count > 0 && GameData.DuelIndex >= GameData.Duels.Count; }
}
public CRouletteDuel CurrentDuel
{
get { return IsFinished || GameData.Duels.Count == 0 ? null : GameData.Duels[GameData.DuelIndex]; }
}
public List GetRanking()
{
return GameData.Players.OrderByDescending(player => player.TablePoints).ThenByDescending(player => player.SingPoints).ToList();
}
public static string GetRuleKey(ERule rule)
{
return "TR_RULE_" + rule.ToString().ToUpperInvariant();
}
private IMenu _GetNextScreen()
{
switch (_Stage)
{
case EStage.Config:
return _Screens["CPartyScreenRuleRouletteConfig"];
case EStage.Rules:
return _Screens["CPartyScreenRuleRouletteRules"];
case EStage.Songs:
return _Screens["CPartyScreenRuleRouletteSongs"];
case EStage.Names:
return _Screens["CPartyScreenRuleRouletteNames"];
case EStage.Main:
return _Screens["CPartyScreenRuleRouletteMain"];
case EStage.SongSelection:
return CBase.Graphics.GetScreen(EScreen.Song);
case EStage.Singing:
case EStage.DirectSinging:
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 = GameData.ManualRules ? EStage.Rules : EStage.Songs;
break;
case EStage.Rules:
_Stage = EStage.Songs;
break;
case EStage.Songs:
_Stage = EStage.Names;
break;
case EStage.Names:
_Stage = EStage.Main;
_StartGame();
break;
case EStage.Main:
if (CurrentDuel == null || !GameData.RuleChosen)
{
return;
}
if (_IsDirectRule(CurrentDuel.Rule))
{
_Stage = EStage.DirectSinging;
_PrepareRound(_PickDirectSongs(CurrentDuel.Rule));
}
else
{
_Stage = EStage.SongSelection;
_PrepareSongSelection();
}
break;
case EStage.SongSelection:
_Stage = EStage.Singing;
break;
case EStage.Singing:
case EStage.DirectSinging:
_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.Rules:
_Stage = EStage.Config;
break;
case EStage.Songs:
_Stage = GameData.ManualRules ? EStage.Rules : 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["CPartyScreenRuleRouletteConfig"];
}
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)
{
var freeChoice = CurrentDuel != null && _IsFreeChoiceRule(CurrentDuel.Rule);
if (!freeChoice && (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 duel = CurrentDuel;
if (duel != null && duel.Players[teamNr].Jokers > 0)
{
duel.Players[teamNr].Jokers--;
}
_ScreenSongOptions.Selection.NumJokers[teamNr]--;
_ScreenSongOptions.Selection.RandomOnly = true;
_ScreenSongOptions.Selection.CategoryChangeAllowed = false;
}
public override void SongSelected(int songId)
{
var songIds = new List { songId };
if (CurrentDuel != null && CurrentDuel.Rule == ERule.DoublePack)
{
_SungSongs.Add(songId);
var extra = _PickSongs(song => !song.IsDuet, 1);
songIds.AddRange(extra);
}
_PrepareRound(songIds.ToArray());
Next();
}
public override void LeavingHighscore()
{
for (var i = 0; i < CBase.Game.GetNumSongs(); i++)
{
CBase.Songs.AddPartySongSung(CBase.Game.GetSong(i).Id);
_SungSongs.Add(CBase.Game.GetSong(i).Id);
}
Next();
}
public void SpinWheel(out int resultIndex)
{
resultIndex = -1;
var duel = CurrentDuel;
if (duel == null || GameData.Wheel.Count == 0)
{
return;
}
var open = GameData.Wheel.Where(wheelRule => !GameData.UsedInCycle.Contains(wheelRule)).ToList();
if (open.Count == 0)
{
GameData.UsedInCycle.Clear();
open = new List(GameData.Wheel);
}
var rule = open[CBase.Game.GetRandom(open.Count)];
GameData.UsedInCycle.Add(rule);
resultIndex = GameData.Wheel.IndexOf(rule);
duel.Rule = rule;
duel.Chooser = null;
duel.TargetScore = 0;
if (rule == ERule.TargetScore)
{
duel.TargetScore = 4000 + CBase.Game.GetRandom(41) * 100;
}
else if (rule == ERule.UnderdogChoice)
{
duel.Chooser = _GetWeaker(duel);
}
GameData.RuleChosen = true;
}
private CRoulettePlayer _GetWeaker(CRouletteDuel duel)
{
var a = duel.Players[0];
var b = duel.Players[1];
if (a.TablePoints != b.TablePoints)
{
return a.TablePoints < b.TablePoints ? a : b;
}
if (a.SingPoints != b.SingPoints)
{
return a.SingPoints < b.SingPoints ? a : b;
}
return duel.Players[CBase.Game.GetRandom(2)];
}
private static bool _IsDirectRule(ERule rule)
{
return rule == ERule.Medley || rule == ERule.GoldRush;
}
private static bool _IsFreeChoiceRule(ERule rule)
{
return rule == ERule.FreeChoice || rule == ERule.UnderdogChoice;
}
private static bool _HasGoldenNotes(CSong song)
{
return song.Notes.Voices.Any(voice => voice.Lines.Any(line => line.Notes.Any(note => note.IsGoldenNote)));
}
private void _StartGame()
{
CBase.Songs.ResetSongSung();
_SungSongs.Clear();
_SourceSongs = _GetSourceSongs();
GameData.Players = new List();
foreach (var profileId in GameData.ProfileIds)
{
GameData.Players.Add(new CRoulettePlayer { ProfileId = profileId, Jokers = GameData.NumJokers });
}
_BuildDuels();
_BuildWheel();
GameData.DuelIndex = 0;
GameData.UsedInCycle = new List();
GameData.RuleChosen = false;
GameData.LastDuel = null;
GameData.NumDuelsPlayed = 0;
}
private void _BuildDuels()
{
var numDuels = Math.Max(1, (GameData.Players.Count * GameData.SongsPerPlayer + 1) / PlayersPerDuel);
var counts = GameData.Players.ToDictionary(player => player, player => 0);
var met = new Dictionary();
GameData.Duels = new List();
CRouletteDuel last = null;
for (var d = 0; d < numDuels; d++)
{
CRoulettePlayer bestA = null;
CRoulettePlayer bestB = null;
var bestValue = int.MaxValue;
var players = new List(GameData.Players);
players.Shuffle();
foreach (var a in players)
{
foreach (var b in players)
{
if (a == b)
{
continue;
}
int times;
met.TryGetValue(_PairKey(a, b), out times);
var repeat = last != null && (last.Players.Contains(a) || last.Players.Contains(b)) ? 1 : 0;
var value = (counts[a] + counts[b]) * 100 + times * 10 + repeat;
if (value < bestValue)
{
bestValue = value;
bestA = a;
bestB = b;
}
}
}
var duel = new CRouletteDuel();
duel.Players[0] = bestA;
duel.Players[1] = bestB;
GameData.Duels.Add(duel);
counts[bestA]++;
counts[bestB]++;
var key = _PairKey(bestA, bestB);
int count;
met.TryGetValue(key, out count);
met[key] = count + 1;
last = duel;
}
}
private string _PairKey(CRoulettePlayer a, CRoulettePlayer b)
{
var ia = GameData.Players.IndexOf(a);
var ib = GameData.Players.IndexOf(b);
return Math.Min(ia, ib) + "-" + Math.Max(ia, ib);
}
public bool IsRuleAvailable(ERule rule)
{
switch (rule)
{
case ERule.ShortSong:
case ERule.Blitz:
return _SourceSongs.Count(song => song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_SHORTSONG)) >= MinSongsForRule;
case ERule.Medley:
return _SourceSongs.Count(song => !song.IsDuet && song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY)) >= MinSongsForRule;
case ERule.DuetOnly:
return _SourceSongs.Count(song => song.IsDuet) >= 1;
case ERule.GoldRush:
return _SourceSongs.Count(song => !song.IsDuet && _HasGoldenNotes(song)) >= MinSongsForRule;
default:
return true;
}
}
private void _BuildWheel()
{
var available = Enum.GetValues(typeof(ERule)).Cast().Where(IsRuleAvailable).ToList();
var always = new List();
var random = new List();
foreach (var rule in available)
{
var state = GameData.ManualRules ? GameData.RuleStates[rule] : ERuleState.Random;
if (state == ERuleState.Always)
{
always.Add(rule);
}
else if (state == ERuleState.Random)
{
random.Add(rule);
}
}
always.Shuffle();
random.Shuffle();
var size = Math.Max(Math.Min(always.Count, MaxRulesOnWheel), GameData.NumRulesOnWheel);
var wheel = always.Take(size).ToList();
wheel.AddRange(random.Take(size - wheel.Count));
if (wheel.Count == 0)
{
available.Shuffle();
wheel = available.Take(size).ToList();
}
wheel.Shuffle();
GameData.Wheel = wheel;
}
private List _GetSourceSongs()
{
var songs = new List();
switch (GameData.SongSource)
{
case ESongSource.TR_SONGSOURCE_PLAYLIST:
for (var i = 0; i < CBase.Playlist.GetSongCount(GameData.PlaylistId); i++)
{
var song = CBase.Songs.GetSongById(CBase.Playlist.GetSong(GameData.PlaylistId, i).SongId);
if (song != null)
{
songs.Add(song);
}
}
break;
case ESongSource.TR_SONGSOURCE_ALLSONGS:
songs.AddRange(CBase.Songs.GetSongs());
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.All, -1);
CBase.Songs.SetCategory(GameData.CategoryIndex);
songs.AddRange(CBase.Songs.GetVisibleSongs());
CBase.Songs.SetCategory(-1);
CBase.Songs.SortSongs(oldSorting, CBase.Config.GetTabs(), CBase.Config.GetIgnoreArticles(), string.Empty, EDuetOptions.All, -1);
break;
}
return songs;
}
private List _PickSongs(Func filter, int count)
{
var candidates = _SourceSongs.Where(song => filter(song) && !_SungSongs.Contains(song.Id)).ToList();
if (candidates.Count < count)
{
candidates = _SourceSongs.Where(filter).ToList();
}
candidates.Shuffle();
return candidates.Take(count).Select(song => song.Id).ToList();
}
private int[] _PickDirectSongs(ERule rule)
{
if (rule == ERule.Medley)
{
return _PickSongs(song => !song.IsDuet && song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_MEDLEY), MedleySongs).ToArray();
}
return _PickSongs(song => !song.IsDuet && _HasGoldenNotes(song), 1).ToArray();
}
private void _PrepareSongSelection()
{
var duel = CurrentDuel;
var freeChoice = _IsFreeChoiceRule(duel.Rule);
_ScreenSongOptions.Selection.RandomOnly = !freeChoice;
_ScreenSongOptions.Selection.SelectNextRandomSong = !freeChoice;
_ScreenSongOptions.Sorting.IgnoreArticles = CBase.Config.GetIgnoreArticles();
_ScreenSongOptions.Sorting.DuetOptions = duel.Rule == ERule.DuetOnly ? EDuetOptions.Duets : EDuetOptions.All;
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 noJokers = duel.Rule == ERule.NoNet || freeChoice;
_ScreenSongOptions.Selection.NumJokers = new int[] { noJokers ? 0 : duel.Players[0].Jokers, noJokers ? 0 : duel.Players[1].Jokers };
_ScreenSongOptions.Selection.TeamNames = new string[]
{
CBase.Profiles.GetPlayerName(duel.Players[0].ProfileId),
CBase.Profiles.GetPlayerName(duel.Players[1].ProfileId)
};
}
private bool _PrepareRound(int[] songIds)
{
CBase.Game.Reset();
CBase.Game.ClearSongs();
CBase.Game.SetNumPlayer(PlayersPerDuel);
var players = CBase.Game.GetPlayers();
var duel = CurrentDuel;
if (players == null || players.Length < PlayersPerDuel || duel == null)
{
return false;
}
for (var p = 0; p < PlayersPerDuel; p++)
{
players[p].ProfileId = duel.Players[p].ProfileId;
players[p].VoiceNr = p;
}
var medley = duel.Rule == ERule.Medley;
var shortSong = duel.Rule == ERule.ShortSong || duel.Rule == ERule.Blitz;
foreach (var songId in songIds)
{
var song = CBase.Songs.GetSongById(songId);
EGameMode gameMode;
if (medley)
{
gameMode = EGameMode.TR_GAMEMODE_MEDLEY;
}
else if (song.IsDuet)
{
gameMode = EGameMode.TR_GAMEMODE_DUET;
}
else if (shortSong && song.IsGameModeAvailable(EGameMode.TR_GAMEMODE_SHORTSONG))
{
gameMode = EGameMode.TR_GAMEMODE_SHORTSONG;
}
else if (song.IsGameModeAvailable(GameData.GameMode))
{
gameMode = GameData.GameMode;
}
else
{
gameMode = EGameMode.TR_GAMEMODE_NORMAL;
}
CBase.Game.AddSong(songId, gameMode);
}
return true;
}
private static int _LeadPoints(int lead)
{
if (lead <= 1000)
{
return 1;
}
if (lead <= 4000)
{
return 2;
}
return lead <= 6500 ? 3 : 4;
}
private void _UpdateScores()
{
var duel = CurrentDuel;
if (duel == null)
{
return;
}
var points = CBase.Game.GetPoints();
if (points == null || points.NumRounds == 0)
{
return;
}
var total = new double[PlayersPerDuel];
var golden = new double[PlayersPerDuel];
var lines = new double[PlayersPerDuel];
for (var round = 0; round < points.NumRounds; round++)
{
var res = points.GetPlayer(round, PlayersPerDuel);
if (res == null || res.Length < PlayersPerDuel)
{
return;
}
for (var p = 0; p < PlayersPerDuel; p++)
{
total[p] += res[p].Points;
golden[p] += res[p].PointsGoldenNotes;
lines[p] += res[p].PointsLineBonus;
}
}
var divisor = duel.Rule == ERule.DoublePack ? 1 : points.NumRounds;
var metric = new int[PlayersPerDuel];
for (var p = 0; p < PlayersPerDuel; p++)
{
duel.Scores[p] = (int)Math.Round(total[p] / divisor);
switch (duel.Rule)
{
case ERule.GoldRush:
metric[p] = (int)Math.Round(golden[p] / divisor);
break;
case ERule.PerfectLines:
metric[p] = (int)Math.Round(lines[p] / divisor);
break;
case ERule.TargetScore:
metric[p] = -Math.Abs(duel.Scores[p] - duel.TargetScore);
break;
case ERule.Reverse:
metric[p] = -duel.Scores[p];
break;
default:
metric[p] = duel.Scores[p];
break;
}
}
var weaker = duel.Rule == ERule.Underdog ? _GetWeaker(duel) : null;
_Award(duel, metric, weaker);
for (var p = 0; p < PlayersPerDuel; p++)
{
var player = duel.Players[p];
player.TablePoints = Math.Max(0, player.TablePoints + duel.Awarded[p]);
player.SingPoints += duel.Scores[p];
player.Duels++;
}
duel.Decided = true;
GameData.LastDuel = duel;
GameData.NumDuelsPlayed++;
GameData.DuelIndex++;
GameData.RuleChosen = false;
}
private void _Award(CRouletteDuel duel, int[] metric, CRoulettePlayer weaker)
{
duel.Awarded[0] = 0;
duel.Awarded[1] = 0;
if (duel.Rule == ERule.Teamwork)
{
var together = duel.Scores[0] + duel.Scores[1] > TeamworkScore ? 2 : 0;
duel.Awarded[0] = together;
duel.Awarded[1] = together;
return;
}
if (duel.Rule == ERule.DoubleWin && duel.Scores[0] > DoubleWinScore && duel.Scores[1] > DoubleWinScore)
{
duel.Awarded[0] = 2;
duel.Awarded[1] = 2;
duel.Players[0].Wins++;
duel.Players[1].Wins++;
return;
}
if (metric[0] == metric[1])
{
duel.Awarded[0] = 1;
duel.Awarded[1] = 1;
if (duel.Rule == ERule.JokerRain)
{
duel.Players[0].Jokers++;
duel.Players[1].Jokers++;
}
return;
}
var winner = metric[0] > metric[1] ? 0 : 1;
var loser = 1 - winner;
duel.Players[winner].Wins++;
if (duel.Rule == ERule.JokerRain)
{
duel.Players[winner].Jokers++;
}
switch (duel.Rule)
{
case ERule.DoublePoints:
case ERule.Blitz:
duel.Awarded[winner] = 4;
break;
case ERule.AllOrNothing:
duel.Awarded[winner] = 3;
duel.Awarded[loser] = -1;
break;
case ERule.PointSteal:
duel.Awarded[winner] = 2 + (duel.Players[loser].TablePoints > 0 ? 1 : 0);
duel.Awarded[loser] = duel.Players[loser].TablePoints > 0 ? -1 : 0;
break;
case ERule.LeadCounts:
duel.Awarded[winner] = _LeadPoints(Math.Abs(duel.Scores[0] - duel.Scores[1]));
break;
case ERule.Underdog:
duel.Awarded[winner] = duel.Players[winner] == weaker ? 3 : 2;
break;
default:
duel.Awarded[winner] = 2;
break;
}
}
}
}