// TODO dkonik: Immediately, I don't see us needing this for anything other than stun. // But this paradigm can be generalized to any state. I.e. have a "forceToState" function // which allows any playerto force any other player to a given state. Though if we do this // we will have to have some /// <summary> /// Forces the player to the stun state for everyone /// </summary> /// <param name="startPosition"></param> /// <param name="blowbackVelocity"></param> /// <param name="duration"></param> public void StunNetworked(Vector2 startPosition, Vector2 blowbackVelocity, float duration, bool stolenFrom) { if (CurrentState == State.Stun) { return; } if (photonView.IsMine) { // If we are the local player, just serialize out as usual StunInformation info = GetStateInformationForWriting <StunInformation>(State.Stun); info.StartPosition = startPosition; info.Velocity = blowbackVelocity; info.Duration = duration; info.StolenFrom = stolenFrom; TransitionToState(State.Stun, info); } else { // TODO dkonik: Using AllViaServer so that photon can guarantee ordering. However, this might // not be the right thing to do. The reason I we need to guarantee ordering (or something along those lines) // is because if the situation described in the comment in [StunNetworked_Interal], namely: "Say, for example, // player 1 destroys player 2s tron wall while they are laying it at the same time that player 3 // possesses the ball right by player 2. Both player 1 and player 3 will send an RPC to stun player // 2" happens, then we need to be able to guarantee ordering. photonView.RPC("StunNetworked_Interal", RpcTarget.AllViaServer, startPosition, blowbackVelocity, duration, stolenFrom); } }
private void HandleNewPlayerState(State oldState, State newState) { if (newState == State.Possession) { StartRumble(duration: ballPossessionRumbleDuration); } if (newState == State.Stun) { StunInformation info = stateManager.CurrentStateInformation_Exn <StunInformation>(); if (info.StolenFrom) { StartRumble(duration: stealRumbleDuration); } } }
private void StunNetworked_Interal(Vector2 startPosition, Vector2 blowbackVelocity, float duration, bool stolenFrom, PhotonMessageInfo rpcInfo) { // Situations can arise where two players try to stun third at roughly the same time. Say, for example, // player 1 destroys player 2s tron wall while they are laying it at the same time that player 3 // possesses the ball right by player 2. Both player 1 and player 3 will send an RPC to stun player // 2. Photon guarantees ordering (at least the way we are currently doing it). So we will just have // everyoen ignore the second rpc. // // Or, another situation that might arise is that player 1 stuns player 2, and so sends the RPC out to force // player 2 to stun state. Player 2 might get this, and serialize out the stun information before player 3 // even gets the RPC, in which case we can also ignore it. if (CurrentState == State.Stun) { StunInformation stunInfo = CurrentStateInformation_Exn <StunInformation>(); bool isSameInformation = stunInfo.EventTimeStamp == rpcInfo.timestamp; Debug.LogFormat("Got an RPC to enter stun state while already in stun, ignoring. IsSameInformation: {0}", isSameInformation); return; } if (nonOwnerForcedState) { Debug.Log("Received an RPC forcing to enter Stun state, but we are already in a forced state."); return; } // Just enter stun as usual, except using the rpc timestamp as the timestamp // If we are the owner, this will get serialized out as usual as well, which is what we want. // If we are not, nonOwnerState will get set to true so we will ignore any other updates // from the owner until they confirm the forced state. nonOwnerForcedState = !photonView.IsMine; StunInformation info = GetStateInformationForWriting <StunInformation>(State.Stun); info.StartPosition = startPosition; info.Velocity = blowbackVelocity; info.Duration = duration; info.EventTimeStamp = rpcInfo.timestamp; State oldState = CurrentState; CurrentState = State.Stun; OnStateChange?.Invoke(oldState, CurrentState); }
private void DoStunMovement() { StunInformation info = stateManager.CurrentStateInformation_Exn <StunInformation>(); StopAllMovementCoroutines(false); // If we for some reason transition to the stun state *after* // we were supposed to have finished with the stun state, just put // the player where they would have ended up float timeTravelledSoFar = (float)(PhotonNetwork.Time - info.EventTimeStamp); if (timeTravelledSoFar > info.Duration) { timeTravelledSoFar = info.Duration; } // Calculate the start position based on the time that has elapsed since // the message was sent rb2d.position = info.StartPosition + timeTravelledSoFar * info.Velocity; rb2d.velocity = info.Velocity; }
/// <summary> /// Manages the duration of the stun. /// NOTE: Does not handle the movement. PlayerMovement will take care of managing /// that while in the stun state /// </summary> /// <returns></returns> private IEnumerator Stun() { StunInformation info = playerStateManager.CurrentStateInformation_Exn <StunInformation>(); if (info.StolenFrom) { GameManager.NotificationManager.NotifyMessage(Message.BallWasStolen, this); } float timeSinceCall = (float)(PhotonNetwork.Time - info.EventTimeStamp); if (timeSinceCall < info.Duration) { yield return(new WaitForSeconds(info.Duration - timeSinceCall)); } else { Debug.LogError("Entered stun after Duration. This shouldn't happen or should happen very rarely. Look into this"); } playerStateManager.TransitionToState(State.NormalMovement); }