C#:
/*
*
* Copyright © 2025 headtapper
*
* You may not copy, modify, share, distribute, publish, or sell copies of this software.
*
* You may use this plugin for personal use on your own Rust server if you purchased it from https://codefling.com.
*
* You may not use components of the software's code base for any other purpose.
*
*/
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Newtonsoft.Json;
using System.Collections.Generic;
using System;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("DiceGame", "headtapper", "1.1.4")]
[Description("Dice game where players can bet money and roll a dice to win.")]
public class DiceGame : RustPlugin
{
#region Initialization
[PluginReference]
private Plugin TCBank;
[PluginReference]
private Plugin Economics;
[PluginReference]
private Plugin ServerRewards;
System.Random rng = new System.Random();
Dictionary<ulong, string> Bets = new Dictionary<ulong, string>();
List<ulong> Cooldowns = new List<ulong>();
List<ulong> ActiveRolls = new List<ulong>();
private const string UseDiceGamePermission = "dicegame.use";
private void Init()
{
permission.RegisterPermission(UseDiceGamePermission, this);
}
private void Unload()
{
foreach (BasePlayer p in BasePlayer.activePlayerList)
CuiHelper.DestroyUi(p, "DiceGameGUI");
}
#endregion
#region Configuration
private PluginConfig config;
private class PluginConfig
{
[JsonProperty(PropertyName = "Total possible numbers to roll (Default=100)")]
public int totalPossibleNumbers { get; set; }
[JsonProperty(PropertyName = "Minimum number to win (inclusive) (Default=55)")]
public int winningNumber { get; set; }
[JsonProperty(PropertyName = "Payout multiplier for win (Default=2.0)")]
public float payoutMultiplier { get; set; }
[JsonProperty(PropertyName = "Cooldown between bets in seconds (Default=1.0)")]
public float cooldownDuration { get; set; }
[JsonProperty(PropertyName = "Use TC Bank for payout")]
public bool useTCBankSupport { get; set; }
[JsonProperty(PropertyName = "Use Economics for payout")]
public bool useEconomicsSupport { get; set; }
[JsonProperty(PropertyName = "Use ServerRewards for payout")]
public bool useServerRewardsSupport { get; set; }
[JsonProperty(PropertyName = "GUI panel background color (RGBA 0-1)")]
public string guiPanelBackgroundColor { get; set; }
[JsonProperty(PropertyName = "Show dice rolling numbers instead of rolling instantly")]
public bool slowRollDice { get; set; }
[JsonProperty(PropertyName = "Slow roll speed per number in seconds (Default=0.1)")]
public float slowRollSpeed { get; set; }
}
private PluginConfig GetDefaultConfigValues()
{
return new PluginConfig
{
totalPossibleNumbers = 100,
winningNumber = 55,
payoutMultiplier = 2.0f,
cooldownDuration = 1.0f,
useTCBankSupport = false,
useEconomicsSupport = false,
useServerRewardsSupport = false,
guiPanelBackgroundColor = "0.1 0.1 0.1 0.8",
slowRollDice = false,
slowRollSpeed = 0.1f
};
}
private void SaveConfig() => Config.WriteObject(config, true);
protected override void LoadDefaultConfig()
{
config = GetDefaultConfigValues();
SaveConfig();
}
protected override void LoadConfig()
{
try
{
base.LoadConfig();
config = Config.ReadObject<PluginConfig>();
SaveConfig();
}
catch (Exception ex)
{
LoadDefaultConfig();
}
}
#endregion
#region Localization
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["GameTitle"] = "DICE GAME",
["SetBet"] = "Your bet has been set to ${0}",
["InvalidBet"] = "Invalid bet amount",
["WinBet"] = "You just won ${0}!",
["LoseBet"] = "Bad luck!",
["GameSettings"] = "{0} total numbers | {1} and over win",
["OnCooldown"] = "You're on cooldown!",
["ActiveRoll"] = "You are already rolling the dice!",
["InsufficientWithdraw"] = "You don't have enough money to bet that much",
["EnterBetGUILabel"] = "Enter Bet",
["RollButtonGUILabel"] = "Roll",
["CloseButtonGUILabel"] = "Close",
}, this);
}
#endregion
#region Functions
private bool ProcessWithdraw(BasePlayer player, int amount)
{
bool result = false;
if (config.useTCBankSupport)
result = TCBank.Call<bool>("RemoveFromWallet", player.UserIDString, (int)amount);
else if (config.useEconomicsSupport)
result = Economics.Call<bool>("Withdraw", player.userID, (double)amount);
else if (config.useServerRewardsSupport)
{
var sr_balance = ServerRewards.Call("CheckPoints", player.userID);
if (sr_balance == null) return false;
if (sr_balance is int bal && amount > bal) return false;
var sr_result = ServerRewards.Call("TakePoints", player.userID, amount);
if (sr_result != null && sr_result is bool && (bool)sr_result)
result = true;
}
return result;
}
private void ProcessDeposit(BasePlayer player, int amount)
{
if (config.useTCBankSupport)
TCBank.Call("AddToWallet", player.UserIDString, (int)amount);
else if (config.useEconomicsSupport)
Economics.Call("Deposit", player.userID, (double)amount);
else if (config.useServerRewardsSupport)
ServerRewards.Call("AddPoints", player.userID, amount);
}
private int Roll(int numSides) => rng.Next(1, numSides + 1);
private bool IsWin(int value, int minRoll) => value >= minRoll;
private int CalculatePayout(int bet, float multiplier) => (int)(bet * multiplier);
private void StartGame(BasePlayer player, int bet)
{
if (config.cooldownDuration > 0)
{
if (Cooldowns.Contains(player.userID))
{
player.ChatMessage(lang.GetMessage("OnCooldown", this, player.UserIDString));
return;
}
Cooldowns.Add(player.userID);
timer.Once((float)config.cooldownDuration, () =>
{
Cooldowns.Remove(player.userID);
});
}
if (ActiveRolls.Contains(player.userID))
{
player.ChatMessage(lang.GetMessage("ActiveRoll", this, player.UserIDString));
return;
}
else
ActiveRolls.Add(player.userID);
if (!ProcessWithdraw(player, bet))
{
player.ChatMessage(lang.GetMessage("InsufficientWithdraw", this, player.UserIDString));
ActiveRolls.Remove(player.userID);
return;
}
int roll = Roll(config.totalPossibleNumbers);
string numberColor = "1 1 1 1";
if (config.slowRollDice)
{
int current = 0;
timer.Repeat((float)config.slowRollSpeed, roll, () =>
{
current++;
if (current == roll)
{
numberColor = (IsWin(roll, config.winningNumber)) ? "0 0.75 0 1" : "0.75 0 0 1";
ActiveRolls.Remove(player.userID);
UpdateRollValueGUI(player, current, numberColor);
}
else
UpdateRollValueGUI(player, current, numberColor);
});
}
else
{
numberColor = (roll >= config.winningNumber) ? "0 0.75 0 1" : "0.75 0 0 1";
UpdateRollValueGUI(player, roll, numberColor);
ActiveRolls.Remove(player.userID);
}
float delay = (config.slowRollDice) ? (float)config.slowRollSpeed * (roll) : 0f;
timer.Once(delay, () =>
{
if (IsWin(roll, config.winningNumber))
{
int payout = CalculatePayout(bet, config.payoutMultiplier);
ProcessDeposit(player, payout);
player.ChatMessage(string.Format(lang.GetMessage("WinBet", this, player.UserIDString), payout - bet));
}
else
player.ChatMessage(lang.GetMessage("LoseBet", this, player.UserIDString));
});
}
private void UpdateRollValueGUI(BasePlayer player, int value, string color)
{
CuiElementContainer container = new CuiElementContainer();
container.Add(new CuiLabel
{
Text = { Text = value.ToString(), FontSize = 40, Align = TextAnchor.MiddleCenter, Color = color },
RectTransform = { AnchorMin = "0.4 0.5", AnchorMax = "0.6 0.7" }
}, "DiceGameGUI", "RollValueText", "RollValueText");
CuiHelper.AddUi(player, container);
}
#endregion
#region Chat Commands
[ChatCommand("dice")]
private void DiceGameCommand(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, UseDiceGamePermission)) return;
if (config.useTCBankSupport && TCBank == null)
{
Puts("TCBank is not loaded but is enabled in the configuration. Unable to open dice menu.");
return;
}
if (config.useEconomicsSupport && Economics == null)
{
Puts("Economics is not loaded but is enabled in the configuration. Unable to open dice menu.");
return;
}
if (config.useServerRewardsSupport && ServerRewards == null)
{
Puts("ServerRewards is not loaded but is enabled in the configuration. Unable to open dice menu.");
return;
}
OpenDiceGameGUI(player);
}
#endregion
#region User Interface
private void OpenDiceGameGUI(BasePlayer player, int rollValue = 0, int betAmount = 0)
{
CuiElementContainer container = new CuiElementContainer();
string numberColor = "1 1 1 1";
if (rollValue >= config.winningNumber)
numberColor = "0 0.75 0 1";
else if (rollValue != 0)
numberColor = "0.75 0 0 1";
container.Add(new CuiPanel
{
Image = { Color = config.guiPanelBackgroundColor },
RectTransform = { AnchorMin = "0.3 0.3", AnchorMax = "0.7 0.7" },
CursorEnabled = true
}, "Overlay", "DiceGameGUI");
container.Add(new CuiLabel
{
Text = { Text = lang.GetMessage("GameTitle", this, player.UserIDString), FontSize = 24, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
RectTransform = { AnchorMin = "0.4 0.85", AnchorMax = "0.6 0.95" }
}, "DiceGameGUI");
container.Add(new CuiLabel
{
Text = { Text = string.Format(lang.GetMessage("GameSettings", this, player.UserIDString), config.totalPossibleNumbers, config.winningNumber), FontSize = 16, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
RectTransform = { AnchorMin = "0.2 0.75", AnchorMax = "0.8 0.84" }
}, "DiceGameGUI");
container.Add(new CuiButton
{
Button = { Color = "0 0.75 0 0.9", Command = "rolldice" },
RectTransform = { AnchorMin = "0.4 0.1", AnchorMax = "0.6 0.2" },
Text = { Text = lang.GetMessage("RollButtonGUILabel", this, player.UserIDString), FontSize = 20, Align = TextAnchor.MiddleCenter }
}, "DiceGameGUI");
container.Add(new CuiButton
{
Button = { Color = "0.75 0 0 0.9", Close = "DiceGameGUI" },
RectTransform = { AnchorMin = "0.85 0.87", AnchorMax = "0.98 0.97" },
Text = { Text = lang.GetMessage("CloseButtonGUILabel", this, player.UserIDString), FontSize = 16, Align = TextAnchor.MiddleCenter }
}, "DiceGameGUI");
container.Add(new CuiLabel
{
Text = { Text = rollValue.ToString(), FontSize = 40, Align = TextAnchor.MiddleCenter, Color = numberColor },
RectTransform = { AnchorMin = "0.4 0.5", AnchorMax = "0.6 0.7" }
}, "DiceGameGUI", "RollValueText");
container.Add(new CuiLabel
{
Text = { Text = lang.GetMessage("EnterBetGUILabel", this, player.UserIDString), FontSize = 20, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
RectTransform = { AnchorMin = "0.4 0.37", AnchorMax = "0.6 0.47" }
}, "DiceGameGUI");
container.Add(new CuiPanel
{
Image = { Color = "0 0 0 0.8" },
RectTransform = { AnchorMin = "0.4 0.25", AnchorMax = "0.6 0.35" }
}, "DiceGameGUI");
container.Add(new CuiElement
{
Name = CuiHelper.GetGuid(),
Parent = "DiceGameGUI",
Components =
{
new CuiRectTransformComponent
{
AnchorMin = "0.4 0.25", AnchorMax = "0.6 0.35"
},
new CuiInputFieldComponent
{
Align = TextAnchor.MiddleCenter,
FontSize = 20,
Text = betAmount.ToString(),
Color = $"1 1 1 1",
Command = "setbetvalue",
NeedsKeyboard = true
},
}
});
CuiHelper.AddUi(player, container);
}
[ConsoleCommand("rolldice")]
private void RollDiceFromGUI(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (player == null) return;
if (!Bets.ContainsKey(player.userID)) return;
if (int.TryParse(Bets[player.userID], out int betAmount) && betAmount > 0)
StartGame(player, betAmount);
else
player.ChatMessage(lang.GetMessage("InvalidBet", this, player.UserIDString));
}
[ConsoleCommand("setbetvalue")]
private void SetBet(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (player == null) return;
if (int.TryParse(arg.Args[0], out int betAmount) && betAmount > 0)
player.ChatMessage(string.Format(lang.GetMessage("SetBet", this, player.UserIDString), betAmount));
else
player.ChatMessage(lang.GetMessage("InvalidBet", this, player.UserIDString));
Bets[player.userID] = arg.Args[0];
}
#endregion
}
}