public class AppStateBroker : SingletonBehaviour<AppStateBroker>
{
public bool Verbose;
public RoundsReactiveProperty CurrentRound = new RoundsReactiveProperty();
public ReactiveCollection<QBClient> ClientsList = new ReactiveCollection<QBClient>();
//plc data streams
public Subject<UniRx.Tuple<float[],bool>> P1Result = new Subject<UniRx.Tuple<float[],bool>>();
public Subject<UniRx.Tuple<float[],bool>> P2Result = new Subject<UniRx.Tuple<float[],bool>>();
public Subject<UniRx.Tuple<float[],bool>> P3Result = new Subject<UniRx.Tuple<float[],bool>>();
public Subject<UniRx.Tuple<float[],bool>> P4Result = new Subject<UniRx.Tuple<float[],bool>>();
public BoolReactiveProperty P1TimeWindow = new BoolReactiveProperty();
public BoolReactiveProperty P2TimeWindow = new BoolReactiveProperty();
public BoolReactiveProperty P3TimeWindow = new BoolReactiveProperty();
public BoolReactiveProperty P4TimeWindow = new BoolReactiveProperty();
//from input module
public Subject<Unit> SnapButonObservable = new Subject<Unit>();
//from Com & PLC module
public Subject<ORTCPEventParams> ComReceivingStream = new Subject<ORTCPEventParams>();
public Subject<string> ComOutGoingStream = new Subject<string>();
public Subject<Unit> HikeStream = new Subject<Unit>();
//Triggers to jump btw states
public Animator StateMachineAnimator;
private readonly Action _moveToR1 = () => Instance.StateMachineAnimator.SetTrigger("MoveToR1");
private readonly Action _moveToR2 = () => Instance.StateMachineAnimator.SetTrigger("MoveToR2");
private readonly Action _moveToR3 = () => Instance.StateMachineAnimator.SetTrigger("MoveToR3");
private readonly Action _moveToIdle = () => Instance.StateMachineAnimator.SetTrigger("MoveToIdle");
private readonly Action _moveToFinish = () => Instance.CurrentRound.Value = Rounds.Finish;
private TeamManager _teamManager;
private void Awake()
{
//debug binding
Observable.EveryUpdate()
.Select(_ => GameManager.TargetObject_External)
.DistinctUntilChanged()
.Subscribe(_ => Verbose = (GameManager.TargetObject_External & TargetObject.AppStateBroker) == TargetObject.AppStateBroker)
.AddTo(gameObject);
_teamManager = TeamManager.Instance;
AddingClient().AddTo(gameObject);
OnTimeWindowOpen().AddTo(gameObject);
SnapToStart()
.Concat(HikeAndCheckRounds(Rounds.R1, Rounds.R1_Hike, _moveToR2))
.Concat(HikeAndCheckRounds(Rounds.R2, Rounds.R2_Hike, _moveToR3))
.Concat(HikeAndCheckRounds(Rounds.R3, Rounds.R3_Hike,_moveToFinish))
.Concat(SnapToFinish())
.Subscribe().AddTo(gameObject);
}
private IDisposable OnTimeWindowOpen()
{
P1TimeWindow.Where(open=> open).Subscribe(b =>
{
var client = ClientsList.First(x => x.Lane == 0);
SetScores(client,client.Lane).AddTo(this);
}).AddTo(this);
P2TimeWindow.Where(open=> open).Subscribe(b =>
{
var client = ClientsList.First(x => x.Lane == 1);
SetScores(client,client.Lane).AddTo(this);
}).AddTo(this);
P3TimeWindow.Where(open=> open).Subscribe(b =>
{
var client = ClientsList.First(x => x.Lane == 2);
SetScores(client,client.Lane).AddTo(this);
}).AddTo(this);
return
P4TimeWindow.Where(open=> open).Subscribe(b =>
{
var client = ClientsList.First(x => x.Lane == 3);
SetScores(client,client.Lane).AddTo(this);
}).AddTo(this);
}
public void CloseTimeWindow(int lane)
{
switch (lane)
{
case 0: P1TimeWindow.Value = false; break;
case 1: P2TimeWindow.Value = false; break;
case 2: P3TimeWindow.Value = false; break;
case 3: P4TimeWindow.Value = false; break;
}
}
private IDisposable SetScores(QBClient client, int lane)
{
var playerresult = lane == 0 ? P1Result :
lane == 1 ? P2Result :
lane == 2 ? P3Result : P4Result;
var timeWindow = lane == 0 ? P1TimeWindow :
lane == 1 ? P2TimeWindow :
lane == 2 ? P3TimeWindow : P4TimeWindow;
return
CurrentRound
.Take(1)
.Subscribe(round =>
{
playerresult.Take(1)
.Subscribe(result =>
{
switch (round)
{
case Rounds.R1_Hike:
client.SetR1Score(result.Item2,result.Item1[0]);
if(timeWindow.Value)timeWindow.Value = false;
break;
case Rounds.R2_Hike:
client.SetR2Score(result.Item2,result.Item1[0]);
if(timeWindow.Value)timeWindow.Value = false;
break;
case Rounds.R3_Hike:
client.SetR3Score(result.Item2,result.Item1[0]);
if(timeWindow.Value)timeWindow.Value = false;
break;
}
}).AddTo(this);
})
.AddTo(this);
}
private IDisposable AddingClient()
{
return
ComReceivingStream
.Subscribe(e =>
{
var json = JSON.Parse(e.message);
var state = json["state"].AsInt;
var lane = json["station_id"].AsInt;
var team = json["team"].AsInt;
var isIntheList = false;
if (state != 10) return;
foreach (var qbClient in ClientsList)
{
isIntheList = qbClient.Lane == lane;
}
if (isIntheList)
{
Debug.Log("Player already joined");
}
else
{
var playerDataStrings = _teamManager.GetPlayerData(team);
var x = new QBClient(e.clientID, lane, team, playerDataStrings[0], playerDataStrings[1]);
ClientsList.Add(x);
if (Verbose)
Debug.LogFormat("[{0}] Client Added(+) \n id - {1} : lane -{2} : team - {3}",
name, x.ClientId, x.Lane, (TeamType) x.Team);
}
});
}
private UniRx.IObservable<Rounds> SnapToStart()
{
var snap2idlehike =
CurrentRound
.Where(x => x == Rounds.Idle)
.Take(1)
.Do(_ =>
{
Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
.TakeUntil(ClientsList.ObserveCountChanged().Where(x => x > 0))
.DoOnCompleted(() =>
{
SnapButonObservable
.Take(1)
.DoOnCompleted(() =>
{
CurrentRound.Value = Rounds.Idle_Hike;
ComOutGoingStream.OnNext("{\"state\":20,\"description\":\"in_game\"}");
})
.Subscribe()
.AddTo(this);
})
.Subscribe()
.AddTo(this);
}).Debug(Verbose,"[{name}]_snap2idle");
var snap2r1 =
CurrentRound
.Where(x => x == Rounds.Idle_Hike)
.Take(1)
.Do(_ =>
{
Observable.Timer(TimeSpan.FromSeconds(2))
.DoOnCompleted(_moveToR1)
.Subscribe()
.AddTo(this);
}).Debug(Verbose,"[{name}]_snap2r1");
return snap2idlehike.Concat(snap2r1);
}
private UniRx.IObservable<Rounds> SnapToFinish()
{
return
CurrentRound
.Where(x => x == Rounds.Finish)
.Take(1)
.Debug(Verbose,"[{name}]_snap2finish")
.Do(_ =>
{
SnapButonObservable
.Take(1)
.DoOnCompleted(() =>
{
_moveToIdle();
ClientsList.Clear();
ComOutGoingStream.OnNext("{state:30,description:\"game_complete or reset\"}");
SnapToStart()
.Concat(HikeAndCheckRounds(Rounds.R1, Rounds.R1_Hike, _moveToR2))
.Concat(HikeAndCheckRounds(Rounds.R2, Rounds.R2_Hike, _moveToR3))
.Concat(HikeAndCheckRounds(Rounds.R3, Rounds.R3_Hike, _moveToFinish))
.Concat(SnapToFinish())
.Subscribe().AddTo(gameObject);
})
.Subscribe()
.AddTo(this);
});
}
private UniRx.IObservable<Rounds> HikeAndCheckRounds(Rounds initial, Rounds tocheck, params Action[] toInvoke )
{
var initialState = initial;
var checkState = tocheck;
var roundIdx = initial == Rounds.R1 ? 1 : initial== Rounds.R2 ? 2 : 3;
var hike = CurrentRound.Where(x => x == initialState)
.Do(_ => DelayedSanp(1.5f, () => CurrentRound.Value = checkState)).Debug(Verbose,"{name}_"+initial+"_Hike");
var check = CheckRoundsDone(checkState, roundIdx, () => toInvoke.ToList().ForEach(a => a.Invoke()))
.Debug(Verbose,"{name}_"+initial+"_Check");
return hike.Take(1).Concat(check);
}
private void DelayedSanp(float duration, params Action[] rounds)
{
Observable.Timer(TimeSpan.FromSeconds(duration))
.Select(x=>new Unit())
.Concat(SnapButonObservable)
.Skip(1)
.Take(1)
.DoOnCompleted(() => rounds.ToList().ForEach(x=>x.Invoke()))
.Subscribe(_=>HikeStream.OnNext(new Unit()))
.AddTo(gameObject);
}
private UniRx.IObservable<Rounds> CheckRoundsDone(Rounds checkState, int round,params Action[] toInvoke)
{
var toDispose = new Subject<Unit>();
CurrentRound.Where(x => x == checkState)
.Take(1)
.Subscribe(_ =>
{
Observable
.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
.TakeWhile(condition => ClientsList.All(x =>
{
var b = false;
switch (round)
{
case 1: b = x.R1Done; break;
case 2: b = x.R2Done; break;
case 3: b = x.R3Done; break;
}
if (Verbose)
Debug.LogFormat("[{0}] Client status Round{4} :\n lane = {1} team ={2} Result = {3}"
, name, x.Lane, x.Team, x.Result1, round);
return b;
}) != true)
.DoOnCompleted(() =>
{
toDispose.OnCompleted();
toInvoke.ToList().ForEach(x =>x.Invoke());
}).Subscribe();
});
return toDispose.Select(_=>checkState);
}
}