C#:
using System;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Daily Rewards", "0killrest", "1.0.0")]
[Description("Daily login rewards with UI and streak system")]
internal class DailyRewards : RustPlugin
{
#region Configuration
private static ConfigData config;
private class ConfigData
{
public bool Enabled { get; set; } = true;
public bool AutoOpenUIOnJoin { get; set; } = true;
public string UIButtonColor { get; set; } = "#00ff00";
public string UIBackgroundColor { get; set; } = "#1a1a1a";
public List<RewardDay> Rewards { get; set; }
public ConfigData()
{
Rewards = new List<RewardDay>
{
new RewardDay { Day = 1, Items = new List<RewardItem> { new RewardItem { ShortName = "scrap", Amount = 50, Icon = "https://i.imgur.com/3Zv5q8u.png" } }, Title = "Day 1", Description = "Welcome reward!" },
new RewardDay { Day = 2, Items = new List<RewardItem> { new RewardItem { ShortName = "scrap", Amount = 75, Icon = "https://i.imgur.com/3Zv5q8u.png" } }, Title = "Day 2", Description = "Keep going!" },
new RewardDay { Day = 3, Items = new List<RewardItem> { new RewardItem { ShortName = "metal.refined", Amount = 100, Icon = "https://i.imgur.com/7X5G3Zc.png" } }, Title = "Day 3", Description = "Good streak!" },
new RewardDay { Day = 4, Items = new List<RewardItem> { new RewardItem { ShortName = "scrap", Amount = 100, Icon = "https://i.imgur.com/3Zv5q8u.png" } }, Title = "Day 4", Description = "Almost there!" },
new RewardDay { Day = 5, Items = new List<RewardItem> { new RewardItem { ShortName = "ammo.rifle", Amount = 50, Icon = "https://i.imgur.com/YlRvM8c.png" } }, Title = "Day 5", Description = "Half way!" },
new RewardDay { Day = 6, Items = new List<RewardItem> { new RewardItem { ShortName = "scrap", Amount = 150, Icon = "https://i.imgur.com/3Zv5q8u.png" } }, Title = "Day 6", Description = "Almost max!" },
new RewardDay { Day = 7, Items = new List<RewardItem> { new RewardItem { ShortName = "rifle.ak", Amount = 1, Icon = "https://i.imgur.com/tUQpZ7W.png" } }, Title = "Day 7", Description = "JACKPOT! Full week!" }
};
}
}
public class RewardDay
{
public int Day { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public List<RewardItem> Items { get; set; }
}
public class RewardItem
{
public string ShortName { get; set; }
public int Amount { get; set; }
public string Icon { get; set; }
}
protected override void LoadConfig()
{
base.LoadConfig();
config = Config.ReadObject<ConfigData>();
if (config == null)
{
LoadDefaultConfig();
}
}
protected override void LoadDefaultConfig()
{
config = new ConfigData();
PrintWarning("Default configuration created.Edit it in oxide/config/DailyRewards.json");
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
#endregion
#region Data Storage
private Dictionary<ulong, PlayerRewardData> playerData;
private class PlayerRewardData
{
public int CurrentStreak { get; set; } = 0;
public int LastClaimDay { get; set; } = 0;
public DateTime LastClaimDate { get; set; }
public List<int> ClaimedDays { get; set; } = new List<int>();
public bool HasClaimedToday { get; set; } = false;
}
private void LoadData()
{
playerData = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, PlayerRewardData>>("DailyRewards_Data") ?? new Dictionary<ulong, PlayerRewardData>();
}
private void SaveData()
{
Interface.Oxide.DataFileSystem.WriteObject("DailyRewards_Data", playerData);
}
#endregion
#region Hooks
private void Init()
{
LoadData();
permission.RegisterPermission("dailyrewards.admin", this);
}
private void OnServerSave() => SaveData();
private void Unload() => SaveData();
private void OnPlayerInit(BasePlayer player)
{
if (!config.Enabled) return;
// Check if player can claim reward
CheckAndNotifyPlayer(player);
if (config.AutoOpenUIOnJoin)
{
// Small delay to let player fully load
timer.Once(2f, () => ShowRewardUI(player));
}
}
private void CheckAndNotifyPlayer(BasePlayer player)
{
var data = GetPlayerData(player.userID);
bool canClaim = CanClaimReward(player);
if (canClaim && !data.HasClaimedToday)
{
SendReply(player, $"<color=green>🎁 Daily reward available! Type <color=yellow>/daily</color> to claim!</color>");
// Send notification icon
player.SendConsoleCommand("gametip.show_tip", "Daily reward available! Type /daily", 5);
}
}
private bool CanClaimReward(BasePlayer player)
{
var data = GetPlayerData(player.userID);
// First time ever
if (data.LastClaimDate == DateTime.MinValue)
return true;
// Check if already claimed today
if (data.HasClaimedToday)
return false;
// Check if next day
TimeSpan difference = DateTime.UtcNow - data.LastClaimDate;
if (difference.TotalHours >= 20) // Allow 20-24 hour window
{
// Reset streak if more than 48 hours passed
if (difference.TotalHours > 48)
{
data.CurrentStreak = 0;
data.ClaimedDays.Clear();
SaveData();
}
return true;
}
return false;
}
private PlayerRewardData GetPlayerData(ulong userId)
{
if (!playerData.ContainsKey(userId))
{
playerData[userId] = new PlayerRewardData
{
CurrentStreak = 0,
LastClaimDate = DateTime.MinValue,
ClaimedDays = new List<int>(),
HasClaimedToday = false
};
SaveData();
}
return playerData[userId];
}
#endregion
#region UI System
private void ShowRewardUI(BasePlayer player)
{
var data = GetPlayerData(player.userID);
int nextDay = Math.Min(data.CurrentStreak + 1, config.Rewards.Count);
var nextReward = config.Rewards.FirstOrDefault(r => r.Day == nextDay);
if (nextReward == null) return;
bool canClaim = CanClaimReward(player);
// Build UI
CuiHelper.DestroyUi(player, "DailyRewardsUI");
CuiElementContainer container = new CuiElementContainer();
// Main panel background
string mainPanel = container.Add(new CuiPanel
{
Image = { Color = "0 0 0 0.95" },
RectTransform = { AnchorMin = "0.25 0.3", AnchorMax = "0.75 0.7" },
CursorEnabled = true
}, "Overlay", "DailyRewardsUI");
// Title bar
string titleBar = container.Add(new CuiPanel
{
Image = { Color = HexToRGBA(config.UIButtonColor, 0.8f) },
RectTransform = { AnchorMin = "0 0.85", AnchorMax = "1 1" }
}, mainPanel);
container.Add(new CuiText
{
Text = { Text = "🎁 DAILY REWARD", FontSize = 24, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" }
}, titleBar);
// Close button
string closeBtn = container.Add(new CuiButton
{
Button = { Color = "1 0.2 0.2 0.8", Command = "dailyreward.close" },
RectTransform = { AnchorMin = "0.92 0.86", AnchorMax = "0.98 0.98" },
Text = { Text = "✖", FontSize = 18, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" }
}, mainPanel);
// Day info
string dayText = canClaim ? $"Day {nextDay} - Ready to claim!" : $"Day {nextDay} - Already claimed today";
string dayColor = canClaim ? "0 1 0 1" : "1 1 0 1";
container.Add(new CuiText
{
Text = { Text = dayText, FontSize = 20, Align = TextAnchor.MiddleCenter, Color = dayColor },
RectTransform = { AnchorMin = "0 0.7", AnchorMax = "1 0.85" }
}, mainPanel);
container.Add(new CuiText
{
Text = { Text = nextReward.Description, FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "0.8 0.8 0.8 1" },
RectTransform = { AnchorMin = "0 0.6", AnchorMax = "1 0.7" }
}, mainPanel);
// Streak info
container.Add(new CuiText
{
Text = { Text = $"🔥 Current streak: {data.CurrentStreak} days", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 0.6 0 1" },
RectTransform = { AnchorMin = "0 0.52", AnchorMax = "1 0.6" }
}, mainPanel);
// Rewards list
float startY = 0.35f;
float itemHeight = 0.12f;
for (int i = 0; i < nextReward.Items.Count; i++)
{
var item = nextReward.Items[i];
float yPos = startY - (i * itemHeight);
string itemPanel = container.Add(new CuiPanel
{
Image = { Color = HexToRGBA(config.UIBackgroundColor, 0.8f) },
RectTransform = { AnchorMin = $"0.1 {yPos}", AnchorMax = $"0.9 {yPos + itemHeight - 0.01f}" }
}, mainPanel);
// Item icon
container.Add(new CuiElement
{
Parent = itemPanel,
Components = {
new CuiRawImageComponent { Png = GetItemIcon(item.ShortName), Color = "1 1 1 1" },
new CuiRectTransformComponent { AnchorMin = "0.02 0.1", AnchorMax = "0.2 0.9" }
}
});
// Item name and amount
string displayName = GetItemDisplayName(item.ShortName);
container.Add(new CuiText
{
Text = { Text = $"{displayName} x{item.Amount}", FontSize = 16, Align = TextAnchor.MiddleLeft, Color = "1 1 1 1" },
RectTransform = { AnchorMin = "0.22 0", AnchorMax = "0.8 1" }
}, itemPanel);
}
// Claim button
if (canClaim)
{
string claimBtn = container.Add(new CuiButton
{
Button = { Color = HexToRGBA(config.UIButtonColor, 0.9f), Command = "dailyreward.claim" },
RectTransform = { AnchorMin = "0.3 0.05", AnchorMax = "0.7 0.2" },
Text = { Text = "🎁 CLAIM REWARD", FontSize = 18, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" }
}, mainPanel);
}
else
{
container.Add(new CuiText
{
Text = { Text = "⏰ Already claimed today! Come back tomorrow.", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 0.5 0 1" },
RectTransform = { AnchorMin = "0.2 0.05", AnchorMax = "0.8 0.2" }
}, mainPanel);
}
CuiHelper.AddUi(player, container);
}
private string HexToRGBA(string hex, float alpha = 1f)
{
if (string.IsNullOrEmpty(hex)) hex = "#ffffff";
hex = hex.TrimStart('#');
float r = int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
float g = int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
float b = int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
return $"{r} {g} {b} {alpha}";
}
private string GetItemIcon(string shortName)
{
// Return icon URL for item
return $"https://rust.wiki/images/thumb/{shortName}.png/64px-{shortName}.png";
}
private string GetItemDisplayName(string shortName)
{
var itemDef = ItemManager.FindItemDefinition(shortName);
return itemDef != null ? itemDef.displayName.english : shortName;
}
#endregion
#region Core Logic
private void ClaimReward(BasePlayer player)
{
var data = GetPlayerData(player.userID);
if (!CanClaimReward(player))
{
SendReply(player, "<color=red>You already claimed your daily reward today!</color>");
ShowRewardUI(player);
return;
}
int nextDay = data.CurrentStreak + 1;
if (nextDay > config.Rewards.Count)
{
// Max streak reached, either stop or loop
SendReply(player, "<color=yellow>You've claimed all rewards! Starting over!</color>");
data.CurrentStreak = 0;
data.ClaimedDays.Clear();
nextDay = 1;
}
var reward = config.Rewards.FirstOrDefault(r => r.Day == nextDay);
if (reward == null)
{
PrintError($"No reward found for day {nextDay}");
return;
}
// Give items
foreach (var itemReward in reward.Items)
{
ItemDefinition itemDef = ItemManager.FindItemDefinition(itemReward.ShortName);
if (itemDef != null)
{
Item item = ItemManager.Create(itemDef, itemReward.Amount);
if (item != null)
{
player.inventory.GiveItem(item);
}
}
}
// Update data
data.CurrentStreak = nextDay;
data.LastClaimDate = DateTime.UtcNow;
data.HasClaimedToday = true;
if (!data.ClaimedDays.Contains(nextDay))
data.ClaimedDays.Add(nextDay);
SaveData();
// Effects
Effect.server.Run("assets/prefabs/locks/keylock/effects/lock.code.updated.prefab", player.transform.position, Vector3.zero);
// Success message
SendReply(player, $"<color=green>🎉 You claimed Day {nextDay} reward! 🎉</color>");
SendReply(player, $"<color=yellow>🔥 Streak: {data.CurrentStreak} days! Come back tomorrow for more!</color>");
// Close UI and reopen to show next day
CuiHelper.DestroyUi(player, "DailyRewardsUI");
timer.Once(1f, () => ShowRewardUI(player));
// Announce to server on big milestones
if (nextDay == 7 || nextDay == 14 || nextDay == 21 || nextDay == 30)
{
Server.Broadcast($"<color=orange>🌟 {player.displayName} reached a {nextDay}-day login streak! 🌟</color>");
}
}
#endregion
#region Commands
[ChatCommand("daily")]
private void cmdDaily(BasePlayer player, string command, string[] args)
{
if (args.Length > 0 && args[0].ToLower() == "claim")
{
ClaimReward(player);
}
else
{
ShowRewardUI(player);
}
}
[ChatCommand("dailyreset")]
private void cmdResetStreak(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, "dailyrewards.admin"))
{
SendReply(player, "No permission");
return;
}
if (args.Length == 0)
{
SendReply(player, "Usage: /dailyreset <steamID> or 'all'");
return;
}
if (args[0].ToLower() == "all")
{
foreach (var key in playerData.Keys.ToList())
{
playerData[key] = new PlayerRewardData();
}
SaveData();
SendReply(player, "Reset all player daily rewards");
return;
}
if (ulong.TryParse(args[0], out ulong steamId))
{
playerData[steamId] = new PlayerRewardData();
SaveData();
SendReply(player, $"Reset daily rewards for {steamId}");
}
}
[ConsoleCommand("dailyreward.close")]
private void cmdCloseUI(ConsoleSystem.Arg args)
{
BasePlayer player = args.Connection?.player as BasePlayer;
if (player != null)
{
CuiHelper.DestroyUi(player, "DailyRewardsUI");
}
}
[ConsoleCommand("dailyreward.claim")]
private void cmdClaimUI(ConsoleSystem.Arg args)
{
BasePlayer player = args.Connection?.player as BasePlayer;
if (player != null)
{
ClaimReward(player);
}
}
[ConsoleCommand("dailyrewards.reload")]
private void cmdReload(ConsoleSystem.Arg args)
{
if (args.Connection != null && !permission.UserHasPermission(args.Connection.UserId.ToString(), "dailyrewards.admin"))
{
args.ReplyWith("No permission");
return;
}
LoadConfig();
LoadData();
args.ReplyWith("DailyRewards reloaded");
}
#endregion
}
}