rott/kf_sources/FrightScript/Classes/KF_Roulette_Wheel.uc
2026-07-14 20:27:09 +07:00

783 lines
21 KiB
Ucode

/*
--------------------------------------------------------------
KF_Roulette_Wheel
--------------------------------------------------------------
Interactive prop for the 2013 Summer Sideshow map. Players
place bets by throwing dosh onto the table. The game begins
when there are enough bets on the table.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_Roulette_Wheel extends Actor
placeable
dependson(KF_Roulette_Bet_Zone);
const NUMPOCKETS = 37;
const POCKETSPACING = 1771.24; // (65536 / NumPockets)
const NUMBETSNDS = 3;
var int FinalPocket;
/* The Tags of the Roulette zone volumes should all match this name */
var() name TableName;
/* A struct representing a player who has bets on this table */
struct SPlayerTableInfo
{
var PlayerController BettingPlayer;
var int InitialBetSum; // amount this player had on the table as of the first bet.
var int CurrentBetSum; // amount this player has on the table right this minute.
};
/* Array of all players who have bets placed on this table */
var array<SPlayerTableInfo> AllPlayers;
/* A struct representing a pocket on the roulette wheel. */
struct SPocketInfo
{
var byte PocketClr; // 0 = Green, 1=Black , 2 = Red
var int PocketPosition; // Rotation Roll value for this pocket. (RUUs)
};
/* An array that stores all the colours associated with the "Pockets on the roulette wheel. */
var SPocketInfo PocketInfo[NUMPOCKETS];
/* Minimum bet that must be on the table to play */
var() int MinBet;
var() KF_Roulette_Ball Ball;
var float MaxBallSpin,MaxWheelSpin;
var float LastBallSpeedDecrement,BallSpeedDecrementInterval;
var array<int> WinningNumbers;
/* Array of all the areas on this table that players can bet in */
var array<KF_Roulette_Bet_Zone> BetZones;
var float SpinDuration;
/* Percent of SpinDuration that must pass before all betting is closed */
var float BetsClosedTimePct;
var float LastSpinTime;
/* The Roulette wheel is turning and the ball is in motion */
var bool bSpinning;
/* Amount of time after a bet is placed before the wheel starts spinning */
var float SpinCountDown;
var float LastSpinCountDownTime;
/* There are sufficient bets on the table to spin, and the countdown to spin is in progress */
var bool bCountingDownToSpin;
var bool bNotifiedBettingClosed;
/* At least one player left his winnings from a previous spin on the table*/
var bool bLettingItRide;
/* Sounds ===============================================================*/
/* Sound the roulette wheel makes when its spinning around */
var Sound WheelSpinSnd;
var Sound PlaceBetSnds[NUMBETSNDS];
var string PlaceBetSndsRef[NUMBETSNDS];
var string WheelSpinSndRef;
/* This Table is active and open for business */
var bool bActive;
var () const bool bStartActive;
replication
{
reliable if(Role == Role_Authority)
FinalPocket;
}
function PreBeginPlay()
{
PreLoadSounds();
}
function PostBeginPlay()
{
FindBetZones();
SetActive(bStartActive);
}
function PreLoadSounds()
{
local int i;
if ( default.WheelSpinSndRef != "" )
{
WheelSpinSnd = sound(DynamicLoadObject(default.WheelSpinSndRef, class'Sound', true));
}
for(i = 0 ; i < NUMBETSNDS; i ++)
{
if ( default.PlaceBetSndsRef[i] != "" )
{
PlaceBetSnds[i] = sound(DynamicLoadObject(default.PlaceBetSndsRef[i], class'Sound', true));
}
}
}
/* Cache all the 'pieces' of this table */
function FindBetZones()
{
local KF_Roulette_Bet_Zone Zone;
foreach AllActors(class 'KF_Roulette_Bet_Zone', Zone, TableName)
{
BetZones[BetZones.length] = Zone;
Zone.OwningTable = self;
Zone.PayOutAmount = GetPayoutFor(Zone.ZoneType);
}
}
/* Returns true if this table is open for betting */
function bool AcceptNewBets()
{
return bActive && (!bSpinning || Level.TimeSeconds - LastSpinTime < (SpinDuration*BetsClosedTimePct)) ;
}
/* Player placed a bet on this table */
function OnAddBet()
{
StartCountDown();
PlaySound(PlaceBetSnds[Rand(NUMBETSNDS)]);
}
/* bet was rejected */
function OnBetRejected()
{
NotifyBettingClosed();
}
/* Check if there are enough bets on the table to start the countdown to the spin */
function bool StartCountDown(optional bool SuppressNotifications)
{
if(!bCountingDownToSpin && !bSpinning && CheckMinBet(SuppressNotifications))
{
bCountingDownToSpin = true;
LastSpinCountDownTime = Level.TimeSeconds;
SetTimer(SpinCountDown,false);
return true;
}
return false;
}
function AbortCountDown()
{
bLettingItRide = false;
bCountingDownToSpin = false;
SetTimer(0.f,false);
}
function Timer()
{
bCountingDownToSpin = false;
SpinWheel();
}
/* A player placed a bet somewhere on this table. Add him to the game */
function AddPlayer(PlayerController NewPlayer)
{
if(!FindPlayer(NewPlayer))
{
AllPlayers.length = AllPlayers.length + 1;
AllPlayers[AllPlayers.length - 1].BettingPlayer = NewPlayer;
}
StartCountDown();
}
/* A Player was removed from the game - (busted out or took his winnings off the table) */
function RemovePlayer(PlayerController PlayerToRemove)
{
local int PlayerIdx;
if(FindPlayer(PlayerToRemove,PlayerIdx))
{
AllPlayers.Remove(PlayerIdx,1);
}
}
function bool FindPlayer(PlayerController PlayerToFind, optional out int PlayerIdx)
{
local int i;
for(i = 0 ; i < AllPlayers.length ; i ++)
{
if(AllPlayers[i].BettingPlayer == PlayerToFind)
{
PlayerIdx = i;
return true;
}
}
return false;
}
/* Begins the wheel animation */
function SpinWheel()
{
local int i;
if(Level.TimeSeconds - LastSpinTime > SpinDuration)
{
if(WheelSpinSnd != none)
{
PlaySound(WheelSpinSnd);
}
LastSpinTime = Level.TimeSeconds;
log("******* SPINNING THE WHEEL ********** ");
FinalPocket = Rand(arrayCount(PocketInfo));
log(" The ball landed on : "@FinalPocket@GetPocketClr(FinalPocket));
bFixedRotationDir = true;
bRotateToDesired = false;
RotationRate.Yaw = -MaxWheelSpin ;
Ball.StartRolling();
bSpinning = true;
for(i = 0 ; i < BetZones.length ; i ++)
{
BetZones[i].OnWheelSpin();
}
if(bLettingItRide)
{
NotifyLetItRide();
}
UpdatePlayerBetTotals();
}
}
simulated function int GetCurrentPocket()
{
return GetPocketAtPosition(RUUToPosition(Ball.Rotation.Yaw - Rotation.Yaw));
}
/* Toggle Table Active or not*/
function SetActive(bool On)
{
if(Ball != none)
{
Ball.bHidden = !On;
}
bHidden = !On;
bActive = On;
if(!bActive)
{
if(bCountingDownToSpin)
{
AbortCountDown();
}
OnSpinComplete();
}
}
function Tick(Float DeltaTime)
{
local int i;
/* Betting is closed */
if(!AcceptNewBets() && !bNotifiedBettingClosed)
{
bNotifiedBettingClosed = true;
NotifyBettingClosed();
}
if(bCountingDownToSpin)
{
/* Someone picked up their winnings */
if(!CheckMinBet())
{
AbortCountDown();
}
for(i = 0 ; i < AllPlayers.Length ; i ++)
{
if(AllPlayers[i].BettingPlayer != none)
{
AllPlayers[i].BettingPlayer.ReceiveLocalizedMessage(class 'Msg_RouletteCountDown',int(SpinCountDown - (Level.TimeSeconds - LastSpinCountDownTime)));
}
}
}
if(bSpinning)
{
/* if(Level.TimeSeconds - LastBallSpeedDecrement > BallSpeedDecrementInterval)
{
LastBallSpeedDecrement = Level.TimeSeconds;
Ball.RotationRate.Yaw = FMax(Ball.RotationRate.Yaw - ((MaxBallSpin * BallSpeedDecrementInterval)/SpinDuration),MaxBallSpin*0.01);
}
*/
if(Level.TimeSeconds - LastSpinTime >= SpinDuration )
{
OnSpinComplete();
}
}
}
function int GetPocketAtPosition(int InPos)
{
local int i;
for(i = 0 ; i < NUMPOCKETS ; i ++)
{
if(PocketInfo[i].PocketPosition == InPos)
{
return i ;
}
}
return -1;
}
/* Converts the wheel position integer to a Unreal Unit Rotation value */
function float PositionToRUU(int Position)
{
return (Position * PocketSpacing) & 65536;
}
/* Converts a Rotation value to a wheel position integer */
function int RUUToPosition(float RotVal)
{
return FClamp(Round(RotVal / PocketSpacing),0,NumPockets-1) ;
}
/* Called when the ball has come to a rest on the desired number. */
function OnSpinComplete()
{
RotationRate.Yaw = 0.f;
Ball.DesiredRotation.Yaw = PositionToRUU(FinalPocket);
Ball.StopRolling();
bFixedRotationDir = false;
bRotateToDesired = true;
DesiredRotation.Yaw = 0.f;
bSpinning = false;
bNotifiedBettingClosed = false;
WinningNumbers[WinningNumbers.length] = FinalPocket;
ProcessBets();
}
static function string GetPocketClr(int Pocket)
{
local int ClrIdx;
local string ClrString;
ClrIdx = default.PocketInfo[Pocket].PocketClr;
switch(ClrIdx)
{
case 0 : ClrString = "Green" ; break;
case 1 : ClrString = "Black" ; break;
case 2 : ClrString = "Red" ; break;
}
return ClrString;
}
/* Returns true if there are enough bets on the table to spin the wheel */
function bool CheckMinBet(optional bool SuppressNotification)
{
local int i;
local int BetTotal;
local bool EnoughCash;
for(i = 0 ; i < BetZones.length ; i ++)
{
BetTotal += BetZones[i].GetBetTotal();
}
EnoughCash = BetTotal >= MinBet;
if(!EnoughCash && !SuppressNotification)
{
NotifyNeedMinBet();
}
return EnoughCash;
}
/* Returns the payout ratio for a specific type of Bet */
function float GetPayoutFor(KF_Roulette_Bet_Zone.EBetType Bet)
{
switch(Bet)
{
case BET_Straight : return 35.f ; break; // 35 to 1
Default : return 1.f; break; // 1 to 1
}
}
/* Calculates the winnings for each BetZone and clears bets on Zones which didn't hit */
function ProcessBets()
{
local int i;
if(BetZones.length == 0)
{
log("WARNING - No BetZones associated with this table. ");
}
for(i = 0 ; i < BetZones.length ; i ++)
{
if(BetZones[i].ZoneType == BET_Red && IsRed(FinalPocket)) // Red bet
{
log("Red bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if(BetZones[i].ZoneType == BET_Black && IsBlack(FinalPocket)) // Black bet
{
log("Black bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if (BetZones[i].ZoneType == BET_Even && IsEven(FinalPocket))
{
log("Even number bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if (BetZones[i].ZoneType == BET_Odd && !IsEven(FinalPocket))
{
log("Odd number bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if(BetZones[i].ZoneType == BET_1st && IsFirsts(FinalPocket)) // First 12
{
log("Firsts bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if(BetZones[i].ZoneType == BET_2nd && IsSeconds(FinalPocket)) // Second 12
{
log("Seconds bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if(BetZones[i].ZoneType == BET_3rd && IsThirds(FinalPocket)) // Third 12
{
log("Thirds bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if(BetZones[i].ZoneType == BET_Low && IsLow(FinalPocket)) // Low Bet
{
log("Low bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if(BetZones[i].ZoneType == BET_High && IsHigh(FinalPocket)) // High Bet
{
log("High bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else if(BetZones[i].ZoneType == BET_Straight &&
FinalPocket == BetZones[i].ZoneNumber) // Straight number
{
log("Straight Bet Wins on "$FinalPocket);
BetZones[i].PayOut();
}
else
{
BetZones[i].ClearOldBets();
}
}
UpdatePlayerBetTotals();
NotifyWinnings();
RemoveBustedPlayers();
/* Let's see if there's enough cash on the table to start another spin ... */
if(StartCountDown(true))
{
bLettingItRide = true;
}
}
/* Updates the current bet totals for all players on the table */
function UpdatePlayerBetTotals()
{
local int i, NumPlayers;
NumPlayers = AllPlayers.length;
for(i = 0 ; i < NumPlayers ; i ++)
{
AllPlayers[i].CurrentbetSum = GetCurrentBetTotalFor(AllPlayers[i].BettingPlayer);
}
}
/* Returns the total amount of dosh a player has bet on this table at the moment */
function int GetCurrentBetTotalFor( PlayerController Player)
{
local int i;
local int idx;
local int TotalBet;
for(i = 0; i < BetZones.Length ; i ++)
{
for(idx = 0 ; idx < BetZones[i].CurrentBets.length ;idx ++)
{
if(BetZones[i].CurrentBets[idx].BettingPlayer == Player)
{
TotalBet += BetZones[i].CurrentBets[idx].BetPickup.CashAmount ;
}
}
}
return TotalBet;
}
/* Remove any players who have no more bets on the table */
function RemoveBustedPlayers()
{
local int i, NumPlayers;
NumPlayers = AllPlayers.length;
for(i = 0 ; i < NumPlayers ; i ++)
{
if(AllPlayers[i].CurrentBetSum <= 0) // this guys went broke, he's not in the game anymore.
{
AllPlayers.Remove(i,1);
}
}
}
/* == Player Feedback & Localized Messaging ======================================================
==================================================================================================*/
/* Let players know how the last spin went. Send them a local message with their net winnings */
function NotifyWinnings()
{
local int i;
local int NetWinnings;
for(i = 0 ; i< AllPlayers.length ; i ++)
{
if(AllPlayers[i].BettingPlayer != none)
{
NetWinnings = AllPlayers[i].CurrentBetSum - AllPlayers[i].InitialBetSum ;
/* let players know which number it landed on */
AllPlayers[i].BettingPlayer.ReceiveLocalizedMessage(class 'Msg_RouletteSpin',FinalPocket);
/* let players know how much they have won so far. */
AllPlayers[i].BettingPlayer.ReceiveLocalizedMessage(class 'Msg_RouletteWinnings', NetWinnings);
}
}
}
/* Let players know that they are letting it ride. */
function NotifyLetitRide()
{
local int i;
for(i = 0 ; i< AllPlayers.length ; i ++)
{
if(AllPlayers[i].BettingPlayer != none)
{
AllPlayers[i].BettingPlayer.ReceiveLocalizedMessage(class 'Msg_RouletteGeneric', 1);
}
}
}
/* Let players know that they need to place more cash on the table to play*/
function NotifyNeedMinBet()
{
local int i;
for(i = 0 ; i< AllPlayers.length ; i ++)
{
if(AllPlayers[i].BettingPlayer != none)
{
AllPlayers[i].BettingPlayer.ReceiveLocalizedMessage(class 'Msg_RouletteGeneric', 3);
}
}
}
/* Let players know that they cannot place anymore bets right now */
function NotifyBettingClosed()
{
local int i;
for(i = 0 ; i< AllPlayers.length ; i ++)
{
if(AllPlayers[i].BettingPlayer != none)
{
AllPlayers[i].BettingPlayer.ReceiveLocalizedMessage(class 'Msg_RouletteGeneric', 2);
}
}
}
/*============================================================================================
=============================================================================================*/
/* Changes the UV2 Material on the Chips so they look glowy to the player who won them
and not glowy to everyone else */
function ClientSetChipMaterial(CashPickup Chips, bool Glow)
{
local int i;
local Material NewMat;
for(i = 0 ; i < AllPlayers.length ; i ++)
{
NewMat = none ;
if(KFPlayerController_Story(AllPlayers[i].BettingPlayer) != none )
{
if(Glow && AllPlayers[i].Bettingplayer == Chips.DroppedBy)
{
NewMat = Chips.default.UV2Texture ;
}
KFPlayerController_Story(AllPlayers[i].BettingPlayer).ClientSetUV2Tex(Chips,NewMat);
}
}
}
function bool IsBlack(int Num)
{
return PocketInfo[Num].PocketClr == 1;
}
function bool IsRed(int Num)
{
return PocketInfo[Num].PocketClr == 2;
}
function bool IsEven(int Num)
{
return Num % 2 == 0;
}
function bool IsFirsts(int Num)
{
return Num <= 12 && Num > 0;
}
function bool IsSeconds(int Num)
{
return Num > 12 && Num <= 24 ;
}
function bool IsThirds( int Num)
{
return Num > 24 && Num <= 36;
}
function bool IsLow (int Num)
{
return Num > 0 && Num <= 18;
}
function bool IsHigh( int Num)
{
return Num >= 19 && Num <= 36 ;
}
defaultproperties
{
DrawType = DT_StaticMesh
StaticMesh = StaticMesh'Pier_SM.Env_Pier_Roulette_Table_Wheel'
Physics = PHYS_Rotating
bFixedRotationDir= true
bCollideActors = true
RemoteRole = Role_SimulatedProxy
MaxBallSpin = 190000
MaxWheelSpin = 10000
MinBet = 100
BetsClosedTimePct = 0.5
SpinDuration = 5
SpinCountDown = 5
BallSpeedDecrementInterval = 0.1
WheelSpinSndRef ="Steamland_SND.Roulette_WheelSpin"
PlaceBetSndsRef(0)="Steamland_SND.Roulette_StackOff_1"
PlaceBetSndsRef(1)="Steamland_SND.Roulette_StackOff_2"
PlaceBetSndsRef(2)="Steamland_SND.Roulette_StackOff_3"
PocketInfo(0)=(PocketClr=0,PocketPosition=32)
PocketInfo(1)=(PocketClr=2,PocketPosition=18)
PocketInfo(2)=(PocketClr=1,Pocketposition=1)
PocketInfo(3)=(PocketClr=2,Pocketposition=30)
PocketInfo(4)=(PocketClr=2,Pocketposition=36)
PocketInfo(5)=(PocketClr=2,PocketPosition=14)
PocketInfo(6)=(PocketClr=1,PocketPosition=5)
PocketInfo(7)=(PocketClr=2,PocketPosition=26)
PocketInfo(8)=(PocketClr=1,PocketPosition=11)
PocketInfo(9)=(PocketClr=2,PocketPosition=22)
PocketInfo(10)=(PocketClr=1,PocketPosition=13)
PocketInfo(11)=(PocketClr=1,PocketPosition=9)
PocketInfo(12)=(PocketClr=2,PocketPosition=28)
PocketInfo(13)=(PocketClr=1,PocketPosition=7)
PocketInfo(14)=(PocketClr=2,PocketPosition=20)
PocketInfo(15)=(PocketClr=1,Pocketposition=34)
PocketInfo(16)=PocketClr=2,PocketPosition=16)
PocketInfo(17)=(PocketClr=1,Pocketposition=3)
PocketInfo(18)=(PocketClr=2,PocketPosition=24)
PocketInfo(19)=(PocketClr=2,Pocketposition=35)
PocketInfo(20)=(PocketClr=1,PocketPosition=19)
PocketInfo(21)=(PocketClr=2,Pocketposition=0)
PocketInfo(22)=(PocketClr=1,Pocketposition=23)
PocketInfo(23)=(PocketClr=2,PocketPosition=12)
PocketInfo(24)=(PocketClr=1,PocketPosition=15)
PocketInfo(25)=(PocketClr=2,Pocketposition=2)
PocketInfo(26)=(PocketClr=1,Pocketposition=31)
PocketInfo(27)=(PocketClr=2,PocketPosition=6)
PocketInfo(28)=(PocketClr=1,PocketPosition=27)
PocketInfo(29)=(PocketClr=1,PocketPosition=25)
PocketInfo(30)=(PocketClr=2,PocketPosition=10)
PocketInfo(31)=(PocketClr=1,PocketPosition=21)
PocketInfo(32)=(PocketClr=2,Pocketposition=33)
PocketInfo(33)=(PocketClr=1,PocketPosition=17)
PocketInfo(34)=(PocketClr=2,Pocketposition=4)
PocketInfo(35)=(PocketClr=1,PocketPosition=29)
PocketInfo(36)=(PocketClr=2,PocketPosition=8)
}