Code/UI/SlotMachineUI.razor.cs
using System;
using System.Linq;
using Sandbox;
using Sandbox.UI;
using Casino.RpSlot.Economy;
namespace Casino.RpSlot;
public partial class SlotMachineUI
{
public static event Action<Transform>? OnPlayerEngaged;
public static event Action? OnPlayerDisengaged;
public SlotMachine Machine { get; set; }
private SlotSymbol[] _displayed = new SlotSymbol[9];
private bool _isEngaged;
private bool[] _reelSpinning = new bool[3];
private float[] _reelStopTimes = new float[3];
private float _spinStartTime;
private SlotState _prevState = SlotState.Idle;
private System.Collections.Generic.List<int> _sortedWinningLines = new();
private int _currentDisplayedLine = -1;
private float _lineDisplayTimer = 0f;
private int _lastSeenWinSignature = -1;
private string _currentTab = "play";
private int _maxGainSession = 0;
private float _insufficientBalanceTimer = 0f;
private bool IsBusy => Machine != null && Machine.State != SlotState.Idle;
public bool IsEngaged
{
get => _isEngaged;
set
{
if ( _isEngaged == value ) return;
_isEngaged = value;
Mouse.Visible = value; // TODO: API obsolete, remplacer quand l'équivalent est stable
if ( value )
{
var viewpoint = Machine?.GameObject.Children
.FirstOrDefault( c => c.Name == "PlayerViewpoint" );
if ( viewpoint != null )
OnPlayerEngaged?.Invoke( new Transform( viewpoint.WorldPosition, viewpoint.WorldRotation ) );
Machine?.GameObject.Components.Get<SlotMachineFx>()?.StartAmbient();
}
else
{
OnPlayerDisengaged?.Invoke();
Machine?.GameObject.Components.Get<SlotMachineFx>()?.StopAmbient();
}
}
}
protected override void OnAwake()
{
base.OnAwake();
Machine = GameObject.Components.GetInParentOrSelf<SlotMachine>();
var rng = new System.Random();
for ( int i = 0; i < _displayed.Length; i++ )
_displayed[i] = (SlotSymbol)rng.Next( 0, 7 );
}
protected override void OnUpdate()
{
if ( Machine == null ) return;
var state = Machine.State;
if ( state == SlotState.Spinning && _prevState != SlotState.Spinning )
{
_spinStartTime = Time.Now;
var rng = new Random();
for ( int i = 0; i < 3; i++ )
{
_reelSpinning[i] = true;
_reelStopTimes[i] = Machine.SpinDuration * (0.5f + (float)rng.NextDouble() * 0.5f);
}
}
if ( state == SlotState.Spinning )
{
var elapsed = Time.Now - _spinStartTime;
var wasSpinning = new bool[_reelSpinning.Length];
for ( int i = 0; i < _reelSpinning.Length; i++ )
wasSpinning[i] = _reelSpinning[i];
for ( int col = 0; col < 3; col++ )
{
if ( elapsed >= _reelStopTimes[col] )
_reelSpinning[col] = false;
}
for ( int col = 0; col < 3; col++ )
{
for ( int row = 0; row < 3; row++ )
{
int idx = col * 3 + row;
if ( _reelSpinning[col] )
_displayed[idx] = (SlotSymbol)((int)(Time.Now * 12 + col * 5 + row * 3) % 7);
else if ( wasSpinning[col] && Machine.ReelResults.Count > idx )
_displayed[idx] = (SlotSymbol)Machine.ReelResults[idx];
}
}
}
else
{
for ( int i = 0; i < 3; i++ ) _reelSpinning[i] = false;
if ( Machine.ReelResults.Count >= 9 )
{
for ( int i = 0; i < 9; i++ )
_displayed[i] = (SlotSymbol)Machine.ReelResults[i];
}
}
_prevState = state;
if ( _insufficientBalanceTimer > 0f )
_insufficientBalanceTimer -= Time.Delta;
if ( state == SlotState.Idle && Machine.ReelResults.Count == 9 )
{
int sig = HashCode.Combine(
Machine.LastPayout,
Machine.WinningLines?.Count ?? 0,
Machine.ReelResults.Count >= 9 ? Machine.ReelResults[0] : -1,
Machine.ReelResults.Count >= 9 ? Machine.ReelResults[4] : -1,
Machine.ReelResults.Count >= 9 ? Machine.ReelResults[8] : -1
);
if ( sig != _lastSeenWinSignature )
{
_lastSeenWinSignature = sig;
_sortedWinningLines.Clear();
_currentDisplayedLine = -1;
_lineDisplayTimer = 0f;
if ( Machine.WinningLines != null )
{
foreach ( var l in Machine.WinningLines )
_sortedWinningLines.Add( l );
}
if ( _sortedWinningLines.Count > 0 )
_currentDisplayedLine = 0;
if ( Machine.LastPayout > _maxGainSession )
_maxGainSession = Machine.LastPayout;
}
if ( _sortedWinningLines.Count > 0 )
{
_lineDisplayTimer += Time.Delta;
if ( _lineDisplayTimer >= 1.5f )
{
_lineDisplayTimer = 0f;
_currentDisplayedLine = ( _currentDisplayedLine + 1 ) % _sortedWinningLines.Count;
}
}
}
else
{
_lastSeenWinSignature = -1;
}
}
private string GetDisplayedSymbol( int i ) => _displayed[i] switch
{
SlotSymbol.Cherry => "🍒",
SlotSymbol.Lemon => "🍋",
SlotSymbol.Orange => "🍊",
SlotSymbol.Bell => "🔔",
SlotSymbol.Bar => "🍫",
SlotSymbol.Seven => "7",
SlotSymbol.Jackpot => "💎",
_ => "?"
};
private int GetPlayerBalance()
{
foreach ( var go in Scene.GetAllObjects( false ) )
foreach ( var c in go.Components.GetAll() )
if ( c is IRpEconomy eco ) return eco.Balance;
return 0;
}
private string GetSymbolClass( int idx ) => _displayed[idx] switch
{
SlotSymbol.Seven => "is-seven",
SlotSymbol.Bar => "is-bar",
_ => ""
};
private string GetWinningColor( int idx )
{
if ( Machine == null ) return "";
if ( Machine.State != SlotState.Idle ) return "";
if ( _currentDisplayedLine < 0 || _sortedWinningLines.Count == 0 ) return "";
var lineIdx = _sortedWinningLines[_currentDisplayedLine];
var line = SlotPaylines.Lines[lineIdx];
if ( line[0] != idx && line[1] != idx && line[2] != idx ) return "";
return lineIdx switch
{
0 => "win-top",
1 => "win-middle",
2 => "win-bottom",
3 => "win-diag-back",
4 => "win-diag-fwd",
_ => ""
};
}
private void PlayClick()
{
Machine?.GameObject.Components.Get<SlotMachineFx>()?.PlayClick();
}
private void SetTab( string tab )
{
PlayClick();
_currentTab = tab;
}
private string GetResultClass()
{
if ( _insufficientBalanceTimer > 0f ) return "active";
if ( Machine == null ) return "";
if ( Machine.State != SlotState.Idle ) return "";
if ( Machine.ReelResults.Count != 9 ) return "";
if ( Machine.LastSpinWasJackpot ) return "active jackpot";
if ( Machine.LastPayout > 0 ) return "active win";
return "active lose";
}
private string FormatMult( float m )
{
if ( m < 1f ) return m.ToString( "0.0" );
return m.ToString( "0" );
}
private void Spin()
{
PlayClick();
if ( !IsEngaged ) return;
if ( IsBusy ) return;
GameObject economyGo = null;
foreach ( var go in Scene.GetAllObjects( false ) )
{
foreach ( var c in go.Components.GetAll() )
{
if ( c is IRpEconomy )
{
economyGo = go;
break;
}
}
if ( economyGo != null ) break;
}
if ( economyGo == null ) return;
var economy = economyGo.Components.GetAll().OfType<IRpEconomy>().FirstOrDefault();
if ( economy != null && economy.Balance < Machine.CurrentBet )
{
_insufficientBalanceTimer = 2f;
return;
}
Machine.RequestSpin( economyGo.Id, Machine.CurrentBet );
}
private void IncreaseBet()
{
if ( IsBusy ) return;
PlayClick();
Machine.CurrentBet = Math.Min( Machine.CurrentBet + 10, Machine.MaxBet );
}
private void DecreaseBet()
{
if ( IsBusy ) return;
PlayClick();
Machine.CurrentBet = Math.Max( Machine.CurrentBet - 10, Machine.MinBet );
}
private void IncreaseBet100()
{
if ( IsBusy ) return;
PlayClick();
Machine.CurrentBet = System.Math.Min( Machine.CurrentBet + 100, Machine.MaxBet );
}
private void DecreaseBet100()
{
if ( IsBusy ) return;
PlayClick();
Machine.CurrentBet = System.Math.Max( Machine.CurrentBet - 100, Machine.MinBet );
}
private void Close()
{
PlayClick();
IsEngaged = false;
}
protected override int BuildHash()
{
var hash = new HashCode();
hash.Add( Machine?.State );
hash.Add( Machine?.CurrentBet );
hash.Add( Machine?.LastPayout );
for ( int i = 0; i < 9; i++ ) hash.Add( _displayed[i] );
hash.Add( _isEngaged );
hash.Add( _reelSpinning[0] );
hash.Add( _reelSpinning[1] );
hash.Add( _reelSpinning[2] );
hash.Add( Machine?.WinningLines?.Count );
hash.Add( _currentDisplayedLine );
hash.Add( _currentTab );
hash.Add( _maxGainSession );
hash.Add( GetPlayerBalance() );
hash.Add( Machine?.LastSpinWasJackpot );
hash.Add( _insufficientBalanceTimer > 0f );
return hash.ToHashCode();
}
}