Skip to content

Commit

Permalink
Merge pull request #72 from NaathySz/main
Browse files Browse the repository at this point in the history
New module: Slot Machine
  • Loading branch information
schwarper authored Jul 21, 2024
2 parents 09fb80c + 4117ea5 commit f214db1
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 0 deletions.
297 changes: 297 additions & 0 deletions Store_Modules/Store_SlotMachine/cs2-store-slotmachine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Commands;
using StoreApi;

public class Store_SlotMachineConfig : BasePluginConfig
{
[JsonPropertyName("min_bet")]
public int MinBet { get; set; } = 10;

[JsonPropertyName("max_bet")]
public int MaxBet { get; set; } = 1000;

[JsonPropertyName("slot_machine_commands")]
public List<string> SlotMachineCommands { get; set; } = [ "slotmachine" ];

[JsonPropertyName("reward_multipliers")]
public Dictionary<string, SlotMachineSymbol> RewardMultipliers { get; set; } = new Dictionary<string, SlotMachineSymbol>
{
{ "★", new SlotMachineSymbol { Multiplier = 10, Chance = 2 } },
{ "♞", new SlotMachineSymbol { Multiplier = 8, Chance = 3 } },
{ "⚓", new SlotMachineSymbol { Multiplier = 6, Chance = 3 } },
{ "☕", new SlotMachineSymbol { Multiplier = 5, Chance = 4 } },
{ "⚽", new SlotMachineSymbol { Multiplier = 4, Chance = 4 } },
{ "☀", new SlotMachineSymbol { Multiplier = 3, Chance = 5 } },
{ "☁", new SlotMachineSymbol { Multiplier = 2, Chance = 5 } },
{ "✿", new SlotMachineSymbol { Multiplier = 15, Chance = 1 } },
{ "☾", new SlotMachineSymbol { Multiplier = 20, Chance = 0.5 } }
};

[JsonPropertyName("slot_timers")]
public SlotTimersConfig SlotTimers { get; set; } = new SlotTimersConfig();

[JsonPropertyName("partial_win_percentage")]
public int PartialWinPercentage { get; set; } = 50;

[JsonPropertyName("sequential_symbols_only")]
public bool SequentialSymbolsOnly { get; set; } = false;

[JsonIgnore]
public List<string> Emojis => RewardMultipliers.Keys.ToList();
}

public class SlotTimersConfig
{
[JsonPropertyName("first_stop")]
public float FirstStop { get; set; } = 1.0f;

[JsonPropertyName("second_stop")]
public float SecondStop { get; set; } = 2.0f;

[JsonPropertyName("third_stop")]
public float ThirdStop { get; set; } = 3.0f;
}

public class SlotMachineSymbol
{
[JsonPropertyName("multiplier")]
public int Multiplier { get; set; }

[JsonPropertyName("chance")]
public double Chance { get; set; }
}

namespace Store_SlotMachine
{
public class Store_SlotMachine : BasePlugin, IPluginConfig<Store_SlotMachineConfig>
{
public override string ModuleName => "Store Module [Slot Machine]";
public override string ModuleVersion => "0.0.1";
public override string ModuleAuthor => "Nathy";

private readonly Random random = new Random();
public IStoreApi? StoreApi { get; set; }
public Store_SlotMachineConfig Config { get; set; } = new Store_SlotMachineConfig();
private readonly ConcurrentDictionary<string, SlotMachineGame> activeGames = new ConcurrentDictionary<string, SlotMachineGame>();

private List<string> emojis = [];

public override void OnAllPluginsLoaded(bool hotReload)
{
StoreApi = IStoreApi.Capability.Get() ?? throw new Exception("StoreApi could not be located.");

emojis = Config.Emojis;

CreateCommands();
RegisterListener<Listeners.OnTick>(OnTick);
}

public void OnConfigParsed(Store_SlotMachineConfig config)
{
config.MinBet = Math.Max(0, config.MinBet);
config.MaxBet = Math.Max(config.MinBet + 1, config.MaxBet);

Config = config;
}

private void CreateCommands()
{
foreach (var cmd in Config.SlotMachineCommands)
{
AddCommand($"css_{cmd}", "Start a slot machine bet", Command_Slot);
}
}

[CommandHelper(minArgs: 1, usage: "<bet_amount>")]
public void Command_Slot(CCSPlayerController? player, CommandInfo info)
{
if (player == null) return;

if (StoreApi == null) throw new Exception("StoreApi could not be located.");

if (!int.TryParse(info.GetArg(1), out int betAmount))
{
info.ReplyToCommand(Localizer["Prefix"] + Localizer["Invalid amount of credits"]);
return;
}

if (betAmount < Config.MinBet)
{
info.ReplyToCommand(Localizer["Prefix"] + Localizer["Minimum bet amount", Config.MinBet]);
return;
}

if (betAmount > Config.MaxBet)
{
info.ReplyToCommand(Localizer["Prefix"] + Localizer["Maximum bet amount", Config.MaxBet]);
return;
}

if (StoreApi.GetPlayerCredits(player) < betAmount)
{
info.ReplyToCommand(Localizer["Prefix"] + Localizer["Not enough credits"]);
return;
}

StartSlotGame(player, betAmount);
}

private void StartSlotGame(CCSPlayerController player, int betAmount)
{
StoreApi!.GivePlayerCredits(player, -betAmount);

List<string> results = new List<string>();
for (int i = 0; i < 3; i++)
{
string symbol = GetRandomSymbol();
results.Add(symbol);
}

var game = new SlotMachineGame(player, betAmount, results);
activeGames[player.SteamID.ToString()] = game;

game.IsInProgress = true;

AddTimer(Config.SlotTimers.FirstStop, () => StopSlot(game, 0));
AddTimer(Config.SlotTimers.SecondStop, () => StopSlot(game, 1));
AddTimer(Config.SlotTimers.ThirdStop, () => StopSlot(game, 2));
}

private void StopSlot(SlotMachineGame game, int slotIndex)
{
if (game == null || !game.IsInProgress) return;

game.StoppedSlots[slotIndex] = true;

if (game.StoppedSlots.All(stopped => stopped))
{
EndSlotGame(game);
}
}

private void EndSlotGame(SlotMachineGame game)
{
if (game == null || !game.IsInProgress) return;

string resultString = string.Join(" ", game.Results);
int multiplier = CalculateMultiplier(game.Results);
int winnings = game.BetAmount * multiplier;

if (multiplier == 1)
{
winnings = game.BetAmount * Config.PartialWinPercentage / 100;
}

StoreApi!.GivePlayerCredits(game.Player, winnings);

if (multiplier > 1)
{
game.Player.PrintToChat(Localizer["Prefix"] + Localizer["Result", resultString]);
game.Player.PrintToChat(Localizer["Prefix"] + Localizer["Winnings", winnings]);
}
else if (multiplier == 1)
{
game.Player.PrintToChat(Localizer["Prefix"] + Localizer["Result", resultString]);
game.Player.PrintToChat(Localizer["Prefix"] + Localizer["2 Symbols", winnings]);
}
else
{
game.Player.PrintToChat(Localizer["Prefix"] + Localizer["Result", resultString]);
game.Player.PrintToChat(Localizer["Prefix"] + Localizer["You lost your bet", game.BetAmount]);
}

game.Player.PrintToCenter(Localizer["Result", resultString]);

activeGames.TryRemove(game.Player.SteamID.ToString(), out _);

game.IsInProgress = false;
}

private int CalculateMultiplier(List<string> results)
{
if (results.All(s => s == results[0]))
{
return Config.RewardMultipliers[results[0]].Multiplier;
}

if (Config.SequentialSymbolsOnly)
{
if (results[0] == results[1] || results[1] == results[2])
{
return 1;
}
}
else
{
if (results[0] == results[1] || results[1] == results[2] || results[0] == results[2])
{
return 1;
}
}

return 0;
}

private string GetRandomSymbol()
{
double totalChance = Config.RewardMultipliers.Sum(kv => kv.Value.Chance);
double randomNumber = random.NextDouble() * totalChance;

foreach (var symbol in Config.RewardMultipliers)
{
if (randomNumber < symbol.Value.Chance)
{
return symbol.Key;
}
randomNumber -= symbol.Value.Chance;
}

return Config.RewardMultipliers.Last().Key;
}

private void OnTick()
{
foreach (var game in activeGames.Values.ToList())
{
if (game.IsInProgress)
{
for (int i = 0; i < game.Results.Count; i++)
{
if (!game.StoppedSlots[i])
{
game.Results[i] = GetRandomSymbol();
}
}

string resultString = string.Join(" ", game.Results);
game.Player.PrintToCenter(resultString);
}
}
}
}

public class SlotMachineGame
{
public CCSPlayerController Player { get; set; }
public int BetAmount { get; set; }
public List<string> Results { get; set; }
public bool IsInProgress { get; set; }
public bool[] StoppedSlots { get; set; }

public SlotMachineGame(CCSPlayerController player, int betAmount, List<string> results)
{
Player = player;
BetAmount = betAmount;
Results = results;
IsInProgress = false;
StoppedSlots = new bool[results.Count];
}
}
}
17 changes: 17 additions & 0 deletions Store_Modules/Store_SlotMachine/cs2-store-slotmachine.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\StoreApi\StoreApi.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

</Project>
11 changes: 11 additions & 0 deletions Store_Modules/Store_SlotMachine/lang/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Prefix": "{red}[Slot Machine] {white}",
"Invalid amount of credits": "Invalid amount of credits. Please enter a valid number",
"Minimum bet amount": "Minimum bet amount is {green}{0} {white}credits",
"Maximum bet amount": "Maximum bet amount is {green}{0} {white}credits",
"Not enough credits": "You don't have enough credits to place this bet",
"Result": "Result: {green}{0}",
"Winnings": "Winnings: {green}{0} {white}credits",
"2 Symbols": "Two matching symbols! Partial reward: {green}{0} {white}credits",
"You lost your bet": "You lost your bet of {green}{0} {white}credits"
}
11 changes: 11 additions & 0 deletions Store_Modules/Store_SlotMachine/lang/pt-BR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Prefix": "{red}[Slot Machine] {white}",
"Invalid amount of credits": "Quantidade inválida de créditos. Por favor, insira um número válido",
"Minimum bet amount": "A quantidade mínima para aposta é {green}{0} {white}créditos",
"Maximum bet amount": "A quantidade máxima para aposta é {green}{0} {white}créditos",
"Not enough credits": "Você não tem créditos o suficiente para essa aposta",
"Result": "Resultado: {green}{0}",
"Winnings": "Ganhos: {green}{0} {white}créditos",
"2 Symbols": "Dois símbolos correspondentes! Recompensa parcial: {green}{0} {white}créditos",
"You lost your bet": "Você perdeu a aposta de {green}{0} {white}créditos"
}

0 comments on commit f214db1

Please sign in to comment.