C#:
// MathQuiz Plugin
// Copyright (c) 2025 ZappyDeV
// Licensed under the MIT License
using System;
using System.Collections.Generic;
using System.IO;
using Oxide.Core;
using Oxide.Game.Rust;
using UnityEngine;
using Newtonsoft.Json;
namespace Oxide.Plugins
{
[Info("MathQuiz", "ZappyDeV", "2.0.4")]
[Description("Periodic math quiz plugin with rewards, multi-language and colorful chat")]
public class MathQuiz : RustPlugin
{
private ConfigData config;
private bool quizRunning = false;
private int answer;
private Timer quizTimer;
private Dictionary<string, string> messages;
#region Config
class ConfigData
{
public int IntervalMinutes { get; set; } = 30;
public string ItemName { get; set; } = "scrap";
public int ItemQuantity { get; set; } = 100;
public bool UseChat { get; set; } = true;
public string Language { get; set; } = "EN";
public int FirstDigits { get; set; } = 2;
public int SecondDigits { get; set; } = 3;
public int QuizDurationSeconds { get; set; } = 300;
}
protected override void LoadDefaultConfig()
{
Config.WriteObject(new ConfigData(), true);
}
#endregion
#region Localization
private void LoadLanguage()
{
string lang = config.Language.ToUpper();
string langFolder = Path.Combine(Interface.Oxide.DataDirectory, "MathQuiz", "Languages");
if (!Directory.Exists(langFolder)) Directory.CreateDirectory(langFolder);
string langFile = Path.Combine(langFolder, $"MathQuiz.{lang}.json");
if (!File.Exists(langFile))
{
var defaultMessages = new Dictionary<string, string>
{
["QuizMessage"] = lang switch
{
"TR" => "Hızlı matematik: {0} + {1} = ? Doğru Cevap için Ödül {2} {3}!",
"GER" => "Schnelle Mathe: {0} + {1} = ? Die erste richtige Antwort gewinnt {2} {3}!",
"CH" => "快速数学:{0} + {1} = ? 第一个正确答案赢得 {2} {3}!",
_ => "Quick math: {0} + {1} = ? First correct answer wins {2} {3}!"
},
["NoWinner"] = lang switch
{
"TR" => "Hiç kimse matematik yarışmasını doğru cevaplayamadı. Bir dahaki sefere şanslı ol!",
"GER" => "Niemand hat das Mathe-Quiz richtig beantwortet. Viel Glück beim nächsten Mal!",
"CH" => "没有人正确回答数学测验。下次好运!",
_ => "No one answered the math quiz correctly. Better luck next time!"
},
["Winner"] = lang switch
{
"TR" => "{0} matematik yarışmasını kazandı! ve Ödülü aldı. Doğru cevap: {1}.",
"GER" => "{0} hat das Mathe-Quiz gewonnen! Richtige Antwort: {1}.",
"CH" => "{0} 赢得了数学测验!答案是 {1}。",
_ => "{0} won the math quiz! The answer was {1}."
},
["ItemReceived"] = lang switch
{
"TR" => "{0} adet {1} aldınız!",
"GER" => "Du hast {0} {1} erhalten!",
"CH" => "你收到了 {0} 个 {1}!",
_ => "You received {0} {1}!"
}
};
File.WriteAllText(langFile, Newtonsoft.Json.JsonConvert.SerializeObject(defaultMessages, Newtonsoft.Json.Formatting.Indented));
}
messages = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(langFile));
}
private string Msg(string key, params object[] args)
{
if (messages != null && messages.ContainsKey(key))
return string.Format(messages[key], args);
return string.Join(" ", args);
}
#endregion
void Init()
{
config = Config.ReadObject<ConfigData>();
LoadLanguage();
Puts($"MathQuiz plugin loaded. Language: {config.Language}");
timer.Repeat(config.IntervalMinutes * 60f, 0, RunQuiz);
}
#region Quiz
private void RunQuiz()
{
if (quizRunning) return;
int firstMin = (int)Math.Pow(10, Math.Max(config.FirstDigits - 1, 0));
int firstMax = (int)Math.Pow(10, config.FirstDigits) - 1;
int a = UnityEngine.Random.Range(firstMin, firstMax + 1);
int secondMin = (int)Math.Pow(10, Math.Max(config.SecondDigits - 1, 0));
int secondMax = (int)Math.Pow(10, config.SecondDigits) - 1;
int b = UnityEngine.Random.Range(secondMin, secondMax + 1);
answer = a + b;
string msg = ColorizeQuizMessage(a, b, config.ItemQuantity, config.ItemName);
PrintToChat(msg);
quizRunning = true;
quizTimer = timer.Once(config.QuizDurationSeconds, EndQuiz);
}
private void EndQuiz()
{
if (!quizRunning) return;
PrintToChat(ColorizeNoWinner());
quizRunning = false;
}
#endregion
#region Chat Handling
object OnPlayerChat(BasePlayer player, string message, ConVar.Chat.ChatChannel channel)
{
if (quizRunning && channel == ConVar.Chat.ChatChannel.Global)
{
if (int.TryParse(message, out int guess) && guess == answer)
{
GiveItem(player);
PrintToChat(ColorizeWinnerMessage(player.displayName, answer));
if (quizTimer != null && !quizTimer.Destroyed)
quizTimer.Destroy();
quizRunning = false;
return false;
}
}
return null;
}
private void GiveItem(BasePlayer player)
{
Item item = ItemManager.CreateByName(config.ItemName, config.ItemQuantity);
if (item != null)
{
player.inventory.GiveItem(item);
SendReply(player, ColorizeItemReceived(config.ItemQuantity, config.ItemName));
}
}
#endregion
#region Chat Color Helpers
private string ColorizeQuizMessage(int a, int b, int qty, string item)
{
string aStr = $"<color=#FFA070>{a}</color>";
string bStr = $"<color=#FFA070>{b}</color>";
string qtyStr = $"<color=#FFA070>{qty}</color>";
string itemStr = $"<color=#FFF3B0>{item}</color>";
return string.Format(Msg("QuizMessage", aStr, bStr, qtyStr, itemStr));
}
private string ColorizeWinnerMessage(string playerName, int answer)
{
return $"<color=#A8E6A3>{string.Format(Msg("Winner", playerName, answer))}</color>";
}
private string ColorizeNoWinner()
{
return $"<color=#F7A8A8>{Msg("NoWinner")}</color>";
}
private string ColorizeItemReceived(int qty, string item)
{
return $"<color=#A0DFFF>{string.Format(Msg("ItemReceived", qty, item))}</color>";
}
#endregion
void Unload()
{
if (quizTimer != null && !quizTimer.Destroyed)
quizTimer.Destroy();
}
}
}