Presentation helper for a soccer match. Watches MatchSimulation state and plays crowd audio, whistles, kicks, headers and card sounds, and tracks home/away/boo reaction values plus a pulse timer.
using Sandbox;
using Socer;
using System;
// Presentation only. Simulation results and career rewards never depend on audio or crowd state.
public sealed class MatchAtmosphere
{
MatchSimulation observed;
readonly float[] kicks=new float[22];
readonly float[] headers=new float[22];
int lastEffect,lastHomeGoals,lastAwayGoals;
bool openingWhistle,halfWhistle,finalWhistle;
float shotCooldown,whistleCooldown,actionCooldown;
public float HomeReaction {get;private set;}
public float AwayReaction {get;private set;}
public float BooReaction {get;private set;}
public float Pulse {get;private set;}
public void PreviewReaction(string kind)
{
if(kind=="cheer")HomeReaction=1;
else if(kind=="away-cheer")AwayReaction=1;
else if(kind=="boo")BooReaction=1;
else if(kind=="reset")HomeReaction=AwayReaction=BooReaction=0;
}
public void Update(MatchController game,float delta)
{
var match=game.Match;
if(observed!=match)
{
observed=match;lastEffect=0;
lastHomeGoals=match.Goals[0];lastAwayGoals=match.Goals[1];
for(int i=0;i<kicks.Length;i++)
{
kicks[i]=i<match.Players.Count?match.Players[i].LastKickAt:0;
headers[i]=i<match.Players.Count?match.Players[i].HeaderAt:0;
}
openingWhistle=match.Seconds>0;halfWhistle=match.SecondHalf;finalWhistle=match.Finished;
HomeReaction=AwayReaction=BooReaction=0;
}
delta=Math.Clamp(delta,0,.1f);
HomeReaction=Math.Max(0,HomeReaction-delta*.22f);
AwayReaction=Math.Max(0,AwayReaction-delta*.22f);
BooReaction=Math.Max(0,BooReaction-delta*.34f);
Pulse+=delta;
shotCooldown=Math.Max(0,shotCooldown-delta);
whistleCooldown=Math.Max(0,whistleCooldown-delta);
actionCooldown=Math.Max(0,actionCooldown-delta);
bool live=!game.ClubOpen && !game.StrategyLabOpen && (!game.IsRulesPreview || game.MediaLeagueActive);
bool textBroadcast=game.QuickBroadcastOpen && !game.IsRulesPreview;
GameAudio.UpdateCrowd(textBroadcast?0:match.Danger,Math.Max(HomeReaction,AwayReaction),live || textBroadcast,delta);
if(!live || game.Replay.Playing)return;
if(!openingWhistle && match.Seconds>0){openingWhistle=true;GameAudio.Whistle();}
if(!halfWhistle && match.SecondHalf){halfWhistle=true;GameAudio.Whistle();}
if(!finalWhistle && match.Finished){finalWhistle=true;GameAudio.Whistle();}
if(match.Goals[0]>lastHomeGoals){HomeReaction=1;BooReaction=.3f;GameAudio.Cheer();}
if(match.Goals[1]>lastAwayGoals){AwayReaction=1;BooReaction=.55f;GameAudio.Cheer();}
lastHomeGoals=match.Goals[0];lastAwayGoals=match.Goals[1];
for(int i=0;i<match.Players.Count && i<kicks.Length;i++)
{
var player=match.Players[i];
if(player.HeaderAt>headers[i]){headers[i]=player.HeaderAt;GameAudio.Header();}
if(player.LastKickAt<=kicks[i])continue;
kicks[i]=player.LastKickAt;
if(player.LastKickWasShot)
{
if(shotCooldown>0)continue;
shotCooldown=.22f;GameAudio.Kick(true,player.LastKickKind);
if(player.Team==0)HomeReaction=Math.Max(HomeReaction,.4f);
else AwayReaction=Math.Max(AwayReaction,.4f);
}
else if(actionCooldown<=0){actionCooldown=.16f;GameAudio.Kick(false,player.LastKickKind);}
}
foreach(var effect in match.Effects)
{
if(effect.Id<=lastEffect)continue;
lastEffect=effect.Id;
switch(effect.Kind)
{
case "offside":
case "yellow-card":
case "red-card":
BooReaction=Math.Max(BooReaction,effect.Kind=="red-card"?1:.7f);
if(whistleCooldown<=0){GameAudio.Whistle();whistleCooldown=1.2f;}
if(effect.Kind is "yellow-card" or "red-card")GameAudio.Card(effect.Kind=="red-card");
break;
case "save":
case "block":
if(actionCooldown<=0){GameAudio.Save();actionCooldown=.16f;}
if(effect.Kind=="save")GameAudio.Cheer();
if(effect.Player>=0 && effect.Player<match.Players.Count)
{
if(match.Players[effect.Player].Team==0)HomeReaction=Math.Max(HomeReaction,.55f);
else AwayReaction=Math.Max(AwayReaction,.55f);
}
break;
case "tackle":
case "intercept":
if(actionCooldown<=0){GameAudio.Tackle();actionCooldown=.13f;}
break;
}
}
}
}