PlayerDialogueRare.cs

This class manages rare dialogue events during match simulation. It handles self-reproach for player mistakes, quiet observations from defenders during downtime, and teammate goal jokes.

Rough Code
using System;
using System.Linq;

namespace Socer;

public sealed partial class MatchSimulation
{
    float nextSelfReproachAt,nextDefenderIdleAt=35,nextQuietSampleAt,nextGoalCommentAt;
    readonly float[] defenderQuietSince=Enumerable.Repeat(-1f,22).ToArray();
    bool suppressGoalDialogueReply;
    int lastGoalCommentTicket=-1;
    public int SelfReproachLines,DefenderIdleConversations,GoalTeammateJokes;

    PlayerSpeechCue RareSelfCue(Footballer p,PlayerSpeechCue cue)
    {
        if(cue is not (PlayerSpeechCue.Miss or PlayerSpeechCue.ControlMistake) || Seconds<nextSelfReproachAt)return cue;
        uint roll=PlayerDialogue.StableHash(p.Profile.CatalogId+":self")^(uint)(Seed*397+p.Id*53+p.SpeechSerial*109);
        return roll%100<35?PlayerSpeechCue.SelfReproach:cue;
    }

    void RareDialogueSpoken(PlayerSpeechCue cue)
    {
        if(cue==PlayerSpeechCue.SelfReproach){SelfReproachLines++;nextSelfReproachAt=Seconds+28;}
        if(cue==PlayerSpeechCue.DefenderIdle)
        {
            DefenderIdleConversations++;
            nextDefenderIdleAt=Seconds+60+(PlayerDialogue.StableHash(Seed+":quiet:"+DefenderIdleConversations)%16);
        }
    }

    bool QuietDefender(Footballer p)
    {
        if(!p.InPlay || p.Keeper || p.Velocity.Length>=2 || p.DirectOrder!=DirectOrderKind.None ||
            Owner<0 || Players[Owner].Team!=p.Team || World(Ball,p.Team).X<62 || (p.Position-Ball).Length<28)return false;
        string role=p.Profile.SlotRole;
        if(!(role is "CB" or "LB" or "RB" or "LWB" or "RWB") && !(string.IsNullOrEmpty(role) && p.Id%11 is >=1 and <=4))return false;
        foreach(var q in Players)if(q.InPlay && q.Team!=p.Team && (q.Position-p.Position).Length<12)return false;
        return true;
    }

    void ObserveRareDialogue()
    {
        if(!DialogueEnabled || Finished || MedicalActive || ShootoutActive || RehearsingStrategy ||
            GoalHoldRemaining>0 || RestartRemaining>0 || IncidentRemaining>0 || PeriodWalkRemaining>0 || ShotInFlight)
        {Array.Fill(defenderQuietSince,-1);return;}
        if(Seconds<nextQuietSampleAt)return;
        nextQuietSampleAt=Seconds+1.5f;
        // Background chatter waits for a sustained quiet spell and yields to football calls.
        foreach(var p in Players)
        {
            if(!QuietDefender(p)){defenderQuietSince[p.Id]=-1;continue;}
            if(defenderQuietSince[p.Id]<0)defenderQuietSince[p.Id]=Seconds;
        }
        if(Seconds<nextDefenderIdleAt || pendingDialogueReply!=null || Players.Any(p=>p.SpeechUntil>Seconds))return;
        foreach(var p in Players)
        {
            if(defenderQuietSince[p.Id]<0 || Seconds-defenderQuietSince[p.Id]<4.5f)continue;
            uint roll=PlayerDialogue.StableHash(Seed+":idle:"+p.Id)^(uint)((int)(Seconds/1.5f)*193);
            if(roll%100<18 && SpeakPlayer(p,PlayerSpeechCue.DefenderIdle))break;
        }
    }

    bool TryTeammateGoalJoke(Footballer scorer,bool assisted,int passer)
    {
        if(!DialogueEnabled || !scorer.InPlay)return false;
        int ticket=Goals[0]+Goals[1];
        if(ticket==lastGoalCommentTicket)return false;
        lastGoalCommentTicket=ticket;
        if(Seconds<nextGoalCommentAt)return false;
        uint roll=PlayerDialogue.StableHash(scorer.Profile.CatalogId+":goal joke")^(uint)(Seed*193+ticket*397);
        if(roll%100>=28)return false;
        var friend=Players.Where(p=>p.InPlay && !p.Keeper && p.Team==scorer.Team && p.Id!=scorer.Id &&
            (p.Position-scorer.Position).Length<=18)
            .OrderBy(p=>assisted && p.Id==passer?0:1).ThenBy(p=>(p.Position-scorer.Position).Length).FirstOrDefault();
        if(friend==null)return false;
        friend.SpeechCooldown=0;friend.LastSpeechAt=-10;
        if(!SpeakPlayer(friend,PlayerSpeechCue.GoalTeammateJoke,true,true,scorer.Id))return false;
        nextGoalCommentAt=Seconds+70;GoalTeammateJokes++;
        return true;
    }
}