C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// Requires: Economics
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Oxide.Plugins
{
[Info("Coin Flip", "Gachl", "1.0.7")]
[Description("Try your luck by challenging players to a coin flip and betting on it using Economics")]
class CoinFlip : CovalencePlugin
{
#region Internal structures
/// <summary>
/// Store information about a coin flip
/// </summary>
private struct CoinFlipParams
{
/// <summary>
/// Amount of the bet
/// </summary>
internal int Amount;
/// <summary>
/// If set, the coin flip is private, and the target is the opponent that was asked to join.Otherwise null./// </summary>
internal IPlayer Target;
/// <summary>
/// The timer that will cancel the coin flip after the configured timeout
/// </summary>
internal Timer Timer;
}
/// <summary>
/// Message enumeration for localisation
/// </summary>
private enum Messages
{
FLIP_USAGE,
DUPLICATE_PLAYERS,
AMBIGUOUS_PLAYERS,
NO_PLAYERS,
ALREADY_FLIPPING,
COINFLIP_INITIATED,
PRIVATE_COINFLIP_INITIATED,
AMOUNT_TOO_LOW,
BALANCE_TOO_LOW,
COINFLIP_READY,
PRIVATE_COINFLIP_READY,
COINFLIP_CANCELED,
PRIVATE_COINFLIP_CANCELED,
NO_FLIPS,
JOIN_USAGE,
NOT_FLIPPING,
FLIP_JOINED,
FLIP_JOIN,
COUNT_THREE,
COUNT_TWO,
COUNT_ONE,
FLIP,
COIN_LAND,
WIN,
LOSS,
LEFT,
YOURSELF,
PRIVATE,
WIN_ALL,
JOIN_ALL,
CANT_PARTICIPATE
}
#endregion
#region Static and global data
// Random Number Generator
private static Random RNG = new Random();
// Configuration keys
private const string CONFIG_MINIMUM_AMOUNT = "MinimumAmount";
private const string CONFIG_TIMEOUT = "Timeout";
private const string CONFIG_ANNOUNCE_EVERYBODY = "AnnounceEverybody";
private const string CONFIG_PREFIX = "Prefix";
// Permissions
private const string PERMISSION_CREATE = "coinflip.create";
private const string PERMISSION_JOIN = "coinflip.join";
#endregion
#region Data
// Configuration values
private int MinimumAmount => (int)Config[CoinFlip.CONFIG_MINIMUM_AMOUNT];
private int Timeout => (int)Config[CoinFlip.CONFIG_TIMEOUT];
private bool AnnounceEverybody => (bool)Config[CoinFlip.CONFIG_ANNOUNCE_EVERYBODY];
private string Prefix => (string)Config[CoinFlip.CONFIG_PREFIX];
// Economics reference
[PluginReference]
private Plugin Economics;
// All active and unjoined coin flips
private Dictionary<string, CoinFlipParams> coinFlips = new Dictionary<string, CoinFlipParams>();
#endregion
#region Plugin control
protected override void LoadDefaultMessages()
{
// Register all default messages
// English
this.lang.RegisterMessages(new Dictionary<string, string> {
{ Enum.GetName(typeof(Messages), Messages.FLIP_USAGE), $"Usage:{Environment.NewLine}/coinflip Amount{Environment.NewLine}/coinflip Amount PlayerNameOrId" },
{ Enum.GetName(typeof(Messages), Messages.DUPLICATE_PLAYERS), "The name {0} exactly matches {1} players, please use a steamid to identify the player." },
{ Enum.GetName(typeof(Messages), Messages.AMBIGUOUS_PLAYERS), $"The name {{0}} matches {{1}} players, please be more specific:{Environment.NewLine}{{2}}" },
{ Enum.GetName(typeof(Messages), Messages.NO_PLAYERS), "The name {0} matches no players." },
{ Enum.GetName(typeof(Messages), Messages.COINFLIP_INITIATED), "Player {0} has started a coin flip for {1}, type /joinflip {0} to join." },
{ Enum.GetName(typeof(Messages), Messages.PRIVATE_COINFLIP_INITIATED), "Player {0} requested a coin flip with you for {1}, type /joinflip {0} to join." },
{ Enum.GetName(typeof(Messages), Messages.AMOUNT_TOO_LOW), "The minimum amount for a coin flip is {0}." },
{ Enum.GetName(typeof(Messages), Messages.BALANCE_TOO_LOW), "Your balance of {0} is not sufficient for a coin flip for {1}" },
{ Enum.GetName(typeof(Messages), Messages.COINFLIP_READY), "You paid {0} for a coin flip.If nobody joins within {1} minute(s) it will automatically be canceled." },
{ Enum.GetName(typeof(Messages), Messages.PRIVATE_COINFLIP_READY), "You paid {0} for a coin flip with {2}.It will automatically be canceled in {1} minute(s)." },
{ Enum.GetName(typeof(Messages), Messages.COINFLIP_CANCELED), "Your coin flip was canceled and {0} has been refunded." },
{ Enum.GetName(typeof(Messages), Messages.PRIVATE_COINFLIP_CANCELED), "Your coin flip with {1} was canceled and {0} has been refunded." },
{ Enum.GetName(typeof(Messages), Messages.NO_FLIPS), "You have no active coin flips." },
{ Enum.GetName(typeof(Messages), Messages.ALREADY_FLIPPING), "You already have an active coin flip.Use /cancelflip to cancel it." },
{ Enum.GetName(typeof(Messages), Messages.JOIN_USAGE), $"Usage:{Environment.NewLine}/joinflip PlayerNameOrId" },
{ Enum.GetName(typeof(Messages), Messages.NOT_FLIPPING), "There is no coin flip from player {0}." },
{ Enum.GetName(typeof(Messages), Messages.FLIP_JOINED), "Player {0} has joined your coin flip.Prepare to flip!" },
{ Enum.GetName(typeof(Messages), Messages.FLIP_JOIN), "You paid {1} to join the coin flip of player {0}.Prepare to flip!" },
{ Enum.GetName(typeof(Messages), Messages.COUNT_THREE), "3..." },
{ Enum.GetName(typeof(Messages), Messages.COUNT_TWO), "2..." },
{ Enum.GetName(typeof(Messages), Messages.COUNT_ONE), "1..." },
{ Enum.GetName(typeof(Messages), Messages.FLIP), "The coin is in the air!" },
{ Enum.GetName(typeof(Messages), Messages.COIN_LAND), "The coin has landed on the side of {0}." },
{ Enum.GetName(typeof(Messages), Messages.WIN), "Congratulations! You have won {0}!" },
{ Enum.GetName(typeof(Messages), Messages.LOSS), "Too bad! You have lost {0}." },
{ Enum.GetName(typeof(Messages), Messages.LEFT), "Your opponent has forfeited, {0} has been refunded." },
{ Enum.GetName(typeof(Messages), Messages.YOURSELF), "You can not have a coin flip against yourself." },
{ Enum.GetName(typeof(Messages), Messages.PRIVATE), "You can not join this coin flip because it's private." },
{ Enum.GetName(typeof(Messages), Messages.WIN_ALL), "Player {0} has just won {2} in a coin flip against player {1}." },
{ Enum.GetName(typeof(Messages), Messages.JOIN_ALL), "Players {0} and {1} will flip a coin for {2}." },
{ Enum.GetName(typeof(Messages), Messages.CANT_PARTICIPATE), "Player {0} can not participate in coin flips." }
}, this, "en");
// German
this.lang.RegisterMessages(new Dictionary<string, string> {
{ Enum.GetName(typeof(Messages), Messages.FLIP_USAGE), $"Verwendung:{Environment.NewLine}/coinflip Betrag{Environment.NewLine}/coinflip Betrag SpielerNameOderId" },
{ Enum.GetName(typeof(Messages), Messages.DUPLICATE_PLAYERS), "Der Name {0} trifft auf {1} Spieler exakt zu, bitte verwende die steamid um den Spieler zu identifizieren." },
{ Enum.GetName(typeof(Messages), Messages.AMBIGUOUS_PLAYERS), $"Der Name {{0}} trifft auf {{1}} Spieler zu, bitte gebe den Namen präziser an:{Environment.NewLine}{{2}}" },
{ Enum.GetName(typeof(Messages), Messages.NO_PLAYERS), "Der Name {0} trifft auf keinen Spieler zu." },
{ Enum.GetName(typeof(Messages), Messages.COINFLIP_INITIATED), "Spieler {0} hat einen Münzwurf für {1} begonnen, gebe /joinflip {0} ein um beizutreten." },
{ Enum.GetName(typeof(Messages), Messages.PRIVATE_COINFLIP_INITIATED), "Spieler {0} möchte mit dir einen Münzwurf für {1} machen, gebe /joinflip {0} ein um beizutreten." },
{ Enum.GetName(typeof(Messages), Messages.AMOUNT_TOO_LOW), "Der minimale Einsatz für einen Münzwurf beträgt {0}." },
{ Enum.GetName(typeof(Messages), Messages.BALANCE_TOO_LOW), "Dein Kontostand von {0} ist nicht ausreichend für einen Münzwurf für {1}" },
{ Enum.GetName(typeof(Messages), Messages.COINFLIP_READY), "Du hast {0} für einen Münzwurf bezahlt.Wenn niemand innerhalb von {1} Minute(n) beitritt wird er automatisch storniert." },
{ Enum.GetName(typeof(Messages), Messages.PRIVATE_COINFLIP_READY), "Du hast {0} für einen Münzwurf gegen {2} gezahlt.Er wird automatisch in {1} Minute(n) storniert." },
{ Enum.GetName(typeof(Messages), Messages.COINFLIP_CANCELED), "Dein Münzwurf wurde storniert und {0} zurückgezahlt." },
{ Enum.GetName(typeof(Messages), Messages.PRIVATE_COINFLIP_CANCELED), "Dein Münzwurf gegen {1} wurde storniert und {0} zurückgezahlt." },
{ Enum.GetName(typeof(Messages), Messages.NO_FLIPS), "Du hast keinen aktiven Münzwurf." },
{ Enum.GetName(typeof(Messages), Messages.ALREADY_FLIPPING), "Du hast bereits einen aktiven Münzwurf.Verwende /cancelflip um ihn zu stornieren." },
{ Enum.GetName(typeof(Messages), Messages.JOIN_USAGE), $"Verwendung:{Environment.NewLine}/joinflip SpielerNameOderId" },
{ Enum.GetName(typeof(Messages), Messages.NOT_FLIPPING), "Der Spieler {0} hat keinen Münzwurf." },
{ Enum.GetName(typeof(Messages), Messages.FLIP_JOINED), "Spieler {0} ist einem Münzwurf beigetreten.Bereit machen zum Wurf!" },
{ Enum.GetName(typeof(Messages), Messages.FLIP_JOIN), "Du hast {1} bezahlt um dem Münzwurf gegen Spieler {0} beizutreten.Bereit machen zum Wurf!" },
{ Enum.GetName(typeof(Messages), Messages.COUNT_THREE), "3..." },
{ Enum.GetName(typeof(Messages), Messages.COUNT_TWO), "2..." },
{ Enum.GetName(typeof(Messages), Messages.COUNT_ONE), "1..." },
{ Enum.GetName(typeof(Messages), Messages.FLIP), "Die Münze ist in der Luft!" },
{ Enum.GetName(typeof(Messages), Messages.COIN_LAND), "Die Münze ist auf der Seite von {0} gelandet." },
{ Enum.GetName(typeof(Messages), Messages.WIN), "Glückwunsch! Du hast {0} gewonnen!" },
{ Enum.GetName(typeof(Messages), Messages.LOSS), "Schade! Du hast {0} verloren." },
{ Enum.GetName(typeof(Messages), Messages.LEFT), "Dein Gegner hat aufgegeben, {0} wurde zurückgezahlt." },
{ Enum.GetName(typeof(Messages), Messages.YOURSELF), "Du kannst keinen Münzwurf gegen dich selbst spielen." },
{ Enum.GetName(typeof(Messages), Messages.PRIVATE), "Du kannst diesem Münzwurf nicht beitreten da er Privat ist." },
{ Enum.GetName(typeof(Messages), Messages.WIN_ALL), "Spieler {0} hat gerade {2} in einem Münzwurf gegen Spieler {1} gewonnen." },
{ Enum.GetName(typeof(Messages), Messages.JOIN_ALL), "Die Spieler {0} und {1} werfen eine Münze für {2}." },
{ Enum.GetName(typeof(Messages), Messages.CANT_PARTICIPATE), "Spieler {0} kann nicht an Münzwürfen teilnehmen." }
}, this, "de");
}
protected override void LoadDefaultConfig()
{
// Setup default values for configuration
Config[CoinFlip.CONFIG_MINIMUM_AMOUNT] = 0;
Config[CoinFlip.CONFIG_TIMEOUT] = 5;
Config[CoinFlip.CONFIG_ANNOUNCE_EVERYBODY] = true;
Config[CoinFlip.CONFIG_PREFIX] = "[Coin Flip]";
}
private void Unload()
{
// Refund all active coin flips to prevent loss of balance when un- or reloading the plugin
this.coinFlips.Keys.ToList().ForEach(k => this.cancelCoinFlip(k));
}
#endregion
#region Tool methods
/// <summary>
/// Send a localised chat message to a player
/// </summary>
/// <param name="player">Target player to receive the message</param>
/// <param name="message">The localised message to be sent</param>
/// <param name="args">Additional information of this message</param>
private void sendLocalisedMessage(IPlayer player, Messages message, params object[] args)
{
// Argument validation
if (player == null)
throw new ArgumentNullException("player");
// Get the string representation of the messsage key
string messageKey = Enum.GetName(typeof(Messages), message);
// Send the localised message to the player
player.Reply(this.lang.GetMessage(messageKey, this, player.Id), this.Prefix, args);
}
/// <summary>
/// Send a localised chat message to a player if a condition is true
/// </summary>
/// <param name="player">Target player to receive the message</param>
/// <param name="condition">The condition that has to be true if the message should be sent</param>
/// <param name="message">The localised message to be sent</param>
/// <param name="args">Additional information of this message</param>
/// <returns>The value of the condition</returns>
private bool sendConditionalLocalisedMessage(IPlayer player, bool condition, Messages message, params object[] args)
{
// Argument validation
if (player == null)
throw new ArgumentNullException("player");
if (condition)
this.sendLocalisedMessage(player, message, args);
return condition;
}
/// <summary>
/// Find a player by name or id.Use id match, then exact match, then case insensitive match, then partial match to find a player./// </summary>
/// <param name="requester">Player who receives messages if matching was unsuccessful</param>
/// <param name="targetPlayerNameOrId">Search string to use for finding the player</param>
/// <returns>The player if found, otherwise null</returns>
private IPlayer findPlayer(IPlayer requester, string targetPlayerNameOrId)
{
// Argument validation
if (requester == null)
throw new ArgumentNullException("requester");
// Refuse empty search strings
if (string.IsNullOrEmpty(targetPlayerNameOrId))
throw new ArgumentNullException("targetPlayerNameOrId");
// Get active player list and store as array to dereference the unterlying IEnumerable
// and prevent changes to occur while this method is running.IPlayer[] connectedPlayers = this.covalence.Players.Connected.ToArray();
// Try an id match
IPlayer[] matches = connectedPlayers.Where(p => p.Id == targetPlayerNameOrId).ToArray();
if (matches.Length == 1)
return matches[0];
// Try an exact match
matches = connectedPlayers.Where(p => p.Name == targetPlayerNameOrId).ToArray();
if (matches.Length == 1)
return matches[0];
// This probably happens very rarely but if there are two players with *exactly* the same name force use of steam ID instead of name
else if (this.sendConditionalLocalisedMessage(requester, matches.Length > 1, Messages.DUPLICATE_PLAYERS, targetPlayerNameOrId, matches.Length))
return null;
// Try a case insensitive match
matches = connectedPlayers.Where(p => p.Name.ToLower() == targetPlayerNameOrId.ToLower()).ToArray();
if (matches.Length == 1)
return matches[0];
else if (this.sendConditionalLocalisedMessage(requester, matches.Length > 1, Messages.AMBIGUOUS_PLAYERS, targetPlayerNameOrId, matches.Length, string.Join(Environment.NewLine, matches.Select(p => p.Name).ToArray())))
return null;
// Try a partial match
matches = connectedPlayers.Where(p => p.Name.ToLower().Contains(targetPlayerNameOrId.ToLower())).ToArray();
if (matches.Length == 1)
return matches[0];
else if (this.sendConditionalLocalisedMessage(requester, matches.Length > 1, Messages.AMBIGUOUS_PLAYERS, targetPlayerNameOrId, matches.Length, string.Join(Environment.NewLine, matches.Select(p => p.Name).ToArray())))
return null;
// No matches have returned results
this.sendLocalisedMessage(requester, Messages.NO_PLAYERS, targetPlayerNameOrId);
return null;
}
#endregion
#region Commands
[Command("coinflip"), Permission(CoinFlip.PERMISSION_CREATE)]
private void coinFlipChatCommand(IPlayer player, string command, string[] args)
{
// Validate caller is a player that can interact with Economy
if (player.IsServer)
{
player.Reply("This command can not be used from RCON.");
return;
}
// Validate argument count
if (this.sendConditionalLocalisedMessage(player, args.Length == 0, Messages.FLIP_USAGE))
return;
// Parse bet amount from argument
int amount = 0;
if (this.sendConditionalLocalisedMessage(player, !int.TryParse(args[0], out amount), Messages.FLIP_USAGE))
return;
// Limit bet amount to configured minimum value
if (this.sendConditionalLocalisedMessage(player, amount < this.MinimumAmount || amount < 0, Messages.AMOUNT_TOO_LOW, this.MinimumAmount))
return;
// If a target player was supplied, try to find it
string targetPlayerName = String.Join(" ", args.Skip(1).ToArray());
IPlayer targetPlayer = null;
if (!string.IsNullOrEmpty(targetPlayerName))
{
targetPlayer = this.findPlayer(player, targetPlayerName);
if (targetPlayer == null)
return;
}
// Prevent targeting yourself as private coin flip opponent
if (this.sendConditionalLocalisedMessage(player, targetPlayer != null && player.Id == targetPlayer.Id, Messages.YOURSELF))
return;
// Prevent targeting players who can not participate in coin flips
if (this.sendConditionalLocalisedMessage(player, targetPlayer != null && !targetPlayer.HasPermission(CoinFlip.PERMISSION_JOIN), Messages.CANT_PARTICIPATE, targetPlayer?.Name))
return;
// Prevent player from starting multiple coin flips at once
if (this.coinFlips.ContainsKey(player.Id))
{
this.sendLocalisedMessage(player, Messages.ALREADY_FLIPPING, this.coinFlips[player.Id].Amount);
return;
}
// Check and withdraw balance for coin flip
double availableBalance = this.Economics.Call<double>("Balance", player.Id);
if (this.sendConditionalLocalisedMessage(player, !this.Economics.Call<bool>("Withdraw", player.Id, (double)amount), Messages.BALANCE_TOO_LOW, availableBalance, amount))
return;
// Create active coin flip
this.coinFlips.Add(player.Id, new CoinFlipParams()
{
Amount = amount,
Target = targetPlayer,
// Start a timer that cancels the coin flip after the configured timeout
Timer = timer.Once(60 * this.Timeout, () => this.cancelCoinFlip(player.Id))
});
// Send messages depending on whether a target player was supplied or not
if (targetPlayer == null)
{
// Send messages to all players
this.covalence.Players.Connected.ToArray().Where(p => p.Id != player.Id && p.HasPermission(CoinFlip.PERMISSION_JOIN)).ToList().ForEach(p => this.sendLocalisedMessage(p, Messages.COINFLIP_INITIATED, player.Name, amount));
// Inform initiating player about the coin flip and withdrawal
this.sendLocalisedMessage(player, Messages.COINFLIP_READY, amount, this.Timeout);
}
else
{
// Inform targeted opponent about coin flip
this.sendLocalisedMessage(targetPlayer, Messages.PRIVATE_COINFLIP_INITIATED, player.Name, amount);
// Inform initiator about coin flip and withdrawal
this.sendLocalisedMessage(player, Messages.PRIVATE_COINFLIP_READY, amount, this.Timeout, targetPlayer.Name);
}
}
[Command("joinflip"), Permission(CoinFlip.PERMISSION_JOIN)]
private void joinFlipChatCommand(IPlayer player, string command, string[] args)
{
// Validate caller is a player that can interact with Economy
if (player.IsServer)
{
player.Reply("This command can not be used from RCON.");
return;
}
// Validate argument count
if (this.sendConditionalLocalisedMessage(player, args.Length == 0, Messages.JOIN_USAGE))
return;
// Try and find the target player
IPlayer targetPlayer = this.findPlayer(player, String.Join(" ", args));
if (targetPlayer == null)
return;
// Prevent joining your own coin flip
if (this.sendConditionalLocalisedMessage(player, player.Id == targetPlayer.Id, Messages.YOURSELF))
return;
// Check if target player has an active coin flip
if (this.sendConditionalLocalisedMessage(player, !this.coinFlips.ContainsKey(targetPlayer.Id), Messages.NOT_FLIPPING, targetPlayer.Name))
return;
// Retrieve active coin flip of target player
CoinFlipParams coinFlipParams = this.coinFlips[targetPlayer.Id];
if (this.sendConditionalLocalisedMessage(player, coinFlipParams.Target != null && coinFlipParams.Target.Id != player.Id, Messages.PRIVATE))
return;
// Check and withdraw balance for coin flip
double availableBalance = this.Economics.Call<double>("Balance", player.Id);
if (this.sendConditionalLocalisedMessage(player, !this.Economics.Call<bool>("Withdraw", player.Id, (double)coinFlipParams.Amount), Messages.BALANCE_TOO_LOW, availableBalance, coinFlipParams.Amount))
return;
// Remove coin flip from active coin flips to prevent any other players from joining
this.coinFlips.Remove(targetPlayer.Id);
coinFlipParams.Timer.Destroy();
// Inform both players about the commencing coin flip
this.sendLocalisedMessage(targetPlayer, Messages.FLIP_JOINED, player.Name);
this.sendLocalisedMessage(player, Messages.FLIP_JOIN, targetPlayer.Name, coinFlipParams.Amount);
// Announce the coin flip to everybody else
if (this.AnnounceEverybody)
this.covalence.Players.Connected.ToArray().Where(p => p.Id != player.Id && p.Id != targetPlayer.Id).ToList().ForEach(p => this.sendLocalisedMessage(p, Messages.JOIN_ALL, targetPlayer.Name, player.Name, coinFlipParams.Amount));
// Start 3 second countdown (it's actually 5 seconds, but sssssh)
timer.Once(1, () =>
{
this.sendLocalisedMessage(player, Messages.COUNT_THREE);
this.sendLocalisedMessage(targetPlayer, Messages.COUNT_THREE);
timer.Once(1, () =>
{
this.sendLocalisedMessage(player, Messages.COUNT_TWO);
this.sendLocalisedMessage(targetPlayer, Messages.COUNT_TWO);
timer.Once(1, () =>
{
this.sendLocalisedMessage(player, Messages.COUNT_ONE);
this.sendLocalisedMessage(targetPlayer, Messages.COUNT_ONE);
timer.Once(1, () =>
{
this.sendLocalisedMessage(player, Messages.FLIP);
this.sendLocalisedMessage(targetPlayer, Messages.FLIP);
// Start actually flipping
timer.Once(1, () => this.flip(targetPlayer, player, coinFlipParams));
});
});
});
});
}
[Command("cancelflip"), Permission(CoinFlip.PERMISSION_CREATE)]
private void cancelFlipChatCommand(IPlayer player, string command, string[] args)
{
// Validate caller is a player that can interact with Economy
if (player.IsServer)
{
player.Reply("This command can not be used from RCON.");
return;
}
// Check if player has an active coin flip
if (this.sendConditionalLocalisedMessage(player, !this.coinFlips.ContainsKey(player.Id), Messages.NO_FLIPS))
return;
// Cancel the coin flip
this.cancelCoinFlip(player.Id);
}
#endregion
#region Coin flip control
/// <summary>
/// Flips a coin
/// </summary>
/// <param name="initiator">Player who initiated this coin flip</param>
/// <param name="player">Player who has joined this coin flip</param>
/// <param name="coinFlipParams">Parameters of this coin flip</param>
private void flip(IPlayer initiator, IPlayer player, CoinFlipParams coinFlipParams)
{
// Argument validation
if (initiator == null)
throw new ArgumentNullException("initiator");
if (player == null)
throw new ArgumentNullException("player");
// Check if both opponents are still connected
if (!initiator.IsConnected || !player.IsConnected)
{
// Send abort message
if (initiator.IsConnected)
this.sendLocalisedMessage(initiator, Messages.LEFT, coinFlipParams.Amount);
if (player.IsConnected)
this.sendLocalisedMessage(player, Messages.LEFT, coinFlipParams.Amount);
// Refund balance
this.Economics.Call("Deposit", initiator.Id, (double)coinFlipParams.Amount);
this.Economics.Call("Deposit", player.Id, (double)coinFlipParams.Amount);
return;
}
// Pick a winner and loser by throwing a coin
IPlayer winner, loser = null;
if (CoinFlip.RNG.Next(0, 2) == 1)
{
winner = initiator;
loser = player;
}
else
{
winner = player;
loser = initiator;
}
// Inform both opponents about the result
this.sendLocalisedMessage(initiator, Messages.COIN_LAND, winner.Name);
this.sendLocalisedMessage(player, Messages.COIN_LAND, winner.Name);
// Congratulate the winner, condole the loser
this.sendLocalisedMessage(winner, Messages.WIN, coinFlipParams.Amount * 2);
this.sendLocalisedMessage(loser, Messages.LOSS, coinFlipParams.Amount);
// Deposit the pot to the winner
this.Economics.Call("Deposit", winner.Id, (double)coinFlipParams.Amount * 2.0);
// Announce result to everybody excluding the two opponents
if (this.AnnounceEverybody)
this.covalence.Players.Connected.ToArray().Where(p => p.Id != player.Id && p.Id != initiator.Id).ToList().ForEach(p => this.sendLocalisedMessage(p, Messages.WIN_ALL, winner.Name, loser.Name, coinFlipParams.Amount * 2));
}
/// <summary>
/// Cancel an active coin flip
/// </summary>
/// <param name="userID">User ID of the coin flip that is to be canceled</param>
private void cancelCoinFlip(string userID)
{
// Argument validation
if (String.IsNullOrEmpty(userID))
throw new ArgumentNullException("userID");
// Check if player has an active coin flip
if (!this.coinFlips.ContainsKey(userID))
return;
// Get the coin flip parameters
CoinFlipParams coinFlipParams = this.coinFlips[userID];
// Destroy the timer that cancels the coin flip after the timeout
coinFlipParams.Timer.Destroy();
// Remove the coin flip from the active coin flip list
this.coinFlips.Remove(userID);
// Refund the balance to the player
this.Economics.Call("Deposit", userID, (double)coinFlipParams.Amount);
// Get the player if he is still online
IPlayer player = this.covalence.Players.FindPlayerById(userID);
if (player == null || !player.IsConnected)
return;
// Inform the player about the cancellation
if (coinFlipParams.Target == null)
this.sendLocalisedMessage(player, Messages.COINFLIP_CANCELED, coinFlipParams.Amount);
else
this.sendLocalisedMessage(player, Messages.PRIVATE_COINFLIP_CANCELED, coinFlipParams.Amount, coinFlipParams.Target.Name);
}
#endregion
}
}