IEnumerator RematchPersonalInfoReceived(string inName, int inCue, int inAvatar, int starColor, int starNumber, float TPAScore, int gamesPlayed, int gamesWon, float inCoins, int inStreak)
    {
        while (cueController == null)
        {
            yield return(null);
        }

        yield return(new WaitForEndOfFrame());

        // finally we get to populate info about the other player
        GameManager_script.Instance().PopulateOtherGameProfileInfo(inName, inCue, inAvatar, starColor, starNumber, TPAScore, gamesPlayed, gamesWon, inCoins, inStreak);

        // change player head
        cueController.gamecenter.GetComponent <GameCenter>().ChangeHead2Name();
        cueController.gamecenter.GetComponent <GameCenter>().ChangeHead2Image();

        // change star and other shiites
        cueController.gamecenter.GetComponent <GameCenter>().changeStarImage2(GameManager_script.Instance().otherGameProfileInfo.Star.starType);
        cueController.gamecenter.GetComponent <GameCenter>().changeStarText2("" + GameManager_script.Instance().otherGameProfileInfo.Star.text);

        // stringz ya...
        string centerLabelString = GameManager_script.convertNumberIntoGoodStringFormat(GameManager_script.Instance().rematchYourWinCount, "number") + Localization.Get("SpacedMaoHao") + GameManager_script.convertNumberIntoGoodStringFormat(GameManager_script.Instance().rematchOppoWinCount, "number");

        // reset some middle shiite with the 1:0 or 0:1 shiite
        cueController.gamecenter.GetComponent <GameCenter>().ChangeCenterLabel("Red", centerLabelString);
    }
    public void OnHelpfulTipReceived(string inText)
    {
        if (cueController)
        {
            string nameText = ""; // dajiang hack, special cases, special cases everywhere, and "Skip/Push/Break" are left untouched here coz they already work, LOLz.

            // get some name bullshits
            if (inText == "OutOfTime" || inText == "PocketCueBall" || inText == "FourBallRail" || inText == "RightFirstBall" || inText == "Disconnected")
            {
                nameText = Localization.Get("YourOpponent");
            }

            // see if we should kill everything else
            if (inText == "Disconnected" || inText == "OppoLeftGame")
            {
                cueController.ShowHelpfulTooltipPopup(nameText, inText, true, true);
            }
            else
            {
                cueController.ShowHelpfulTooltipPopup(nameText, inText, true, false);
            }

            // show tips

            // dajiang hack, increment foul scratches on the opponent, really kinda sketchy
            if (inText == "PocketCueBall" || inText == "FourBallRail" || inText == "RightFirstBall" || inText == "BallRailContact")
            {
                GameManager_script.IncrementScratch(cueController.TrackingShotAsPlayerOne, cueController.TrackingShotAsPlayerTwo);
            }
        }
    }
Beispiel #3
0
    // this one resets the cueball's position after it was potted or something
    public void SpotBallOnTableAndHouseKeeping(Vector3 position, Vector3 velocity, Vector3 angular)
    {
        // determine if the position is valid, if not keep on trying to get the closest position possible
        GetComponent <Rigidbody>().position = cueController.givemeagoodposition(position);

        GetComponent <Rigidbody>().isKinematic = false;
        GetComponent <Rigidbody>().useGravity  = true;
        GetComponent <Collider>().enabled      = true;

        GetComponent <Rigidbody>().velocity        = velocity;
        GetComponent <Rigidbody>().angularVelocity = angular;

        if (pocketid != -1)
        {
            PocketController pc = PocketController.FindeHoleById(pocketid);

            pc.IncreaseSplineLength();

            pocketid = -1;
        }

        GameManager_script.DecrementBallPocketed(cueController.TrackingShotAsPlayerOne, cueController.TrackingShotAsPlayerTwo);

        cueController.pocketedBallControllers.Remove(this);
        cueController.pocketedBallControllers.TrimExcess();
    }
Beispiel #4
0
    // this will be called on master as well as slave on a ball pocket, this is when ball triggers the box collider
    void OnTriggerEnter(Collider other)
    {
        BallController ballController = other.GetComponent <BallController>();

        if (ballController)
        {
            if (!ballController.ballIsPocketed)
            {
                // choose pocket sound
                float b_p_volume = Mathf.Clamp01(ballController.GetComponent <Rigidbody>().velocity.magnitude / cueController.ballMaxVelocity);
                int   b_p_index  = Random.Range((int)MusicClip.B_Pocket_0, (int)MusicClip.B_Pocket_4 + 1);

                // play pocket sound
                GameManager_script.Instance().PlaySound(b_p_index, false, b_p_volume);

                StartCoroutine(DelayPlayingRollUnderTableSound(ballController));

                ballController.finalAnimationPlacement   = 0.0f;
                ballController.initialAnimationPlacement = 0.0f;
                DecreaseSplineLength();

                ballController.ballIsPocketed = true;
                ballController.OnSetHoleSpline(splineCurrentLength, initialSplineLength, id);

                OnBallPocketedHouseKeeping(ballController);
            }
        }
    }
Beispiel #5
0
    // calls after a ball has completely been played. For cueball and most of 9 ball, we reset it back up. For object balls, we screw it!
    // there should be more complicated logics that does this. So modify this later
    public void OnBallPocketedHouseKeeping(BallController ballController)
    {
        // cue ball should always be spotted
        if (ballController.isMain)
        {
            // set flags to indicate ball is pocketed
            cueController.pocketedTheCueBall = true;
            cueController.OnCueBallIsPocketed(true);
        }
        else if (ballController.id == 9)
        {
            // set flags to indicate ball is pocketed
            ballController.ballIsPocketed       = true;
            cueController.pocketedNineBall      = true;
            cueController.pocketedAnyObjectBall = true;

            // remove it from currentBallControllers
            cueController.currentBallControllers.Remove(ballController); // don't change this. Because nearest object ball depends on this. Talk with DJ before proceed.
            cueController.currentBallControllers.TrimExcess();
        }
        else
        {
            // set flags to indicate ball is pocketed
            ballController.ballIsPocketed       = true;
            cueController.pocketedAnyObjectBall = true;

            // remove it from currentBallControllers
            cueController.currentBallControllers.Remove(ballController); // don't change this. Because nearest object ball depends on this. Talk with DJ before proceed.
            cueController.currentBallControllers.TrimExcess();
        }

        GameManager_script.IncrementBallPocketed(cueController.TrackingShotAsPlayerOne, cueController.TrackingShotAsPlayerTwo);

        cueController.pocketedBallControllers.Add(ballController);
    }
 public void OnRematchCapabilityCheckReceived(string inKey)
 {
     if (inKey != "")
     {
         GameManager_script.Instance().rematchPasscodeKey = inKey;
     }
 }
 void Rotate3DCamera(CircularSlider circularSlider)
 {
     GameManager_script.Instance().CanControlCue = false;
     transform.Rotate(Vector3.up, -rotationSpeed * camera3dSlider.displacementZ * Time.deltaTime);
     rotation -= 0.5f * rotationSpeed * camera3dSlider.displacementX * Time.deltaTime;
     rotation  = Mathf.Clamp(rotation, minAngle, maxAngle);
     rotator.localRotation = Quaternion.Euler(rotation, 0.0f, 0.0f);
 }
    // helper function
    public void SendRPCToNetworkViewOthers(string message, params object[] args)
    {
        // has to make sure we are NOT in network bot mode, coz we are not supposed to send anything in that mode
        if (myPhotonView && !GameManager_script.Instance().SmartBotInActionGame)
        {
            myPhotonView.RPC(message, PhotonTargets.Others, args); // PhotonTargets.All
        }

        lastMessageTimeStamp = Time.realtimeSinceStartup;
    }
Beispiel #9
0
    IEnumerator DelayPlayingRollUnderTableSound(BallController bc)
    {
        yield return(new WaitForSeconds(0.5f)); // just ball parking this...

        // choose roll under table sound
        float b_r_volume = Mathf.Clamp01(1.0f);
        int   b_r_index  = Random.Range((int)MusicClip.B_Roll_Under, (int)MusicClip.B_Roll_Under + 1);

        // play roll under table sound
        bc.rollUnderTableSound = GameManager_script.Instance().PlaySound(b_r_index, false, b_r_volume);
    }
Beispiel #10
0
    public void OnButtonUp()
    {
        GameManager_script.Instance().DownOnRealButtons = false;

        PlaySound(Up);

        if (isFlipFlop)
        {
            state = !state;
        }

        if (!IsInvoking("SendOnButtonUp"))
        {
            Invoke("SendOnButtonUp", SendTime);
        }
    }
Beispiel #11
0
    void SlideBallPivot(CircularSlider circularSlider)
    {
        if (cueController.NetworkSlaveInControl())
        {
            return;
        }

        GameManager_script.Instance().CanControlCue = false;

        transform.localPosition = new Vector3(-circularSlider.displacementZ, circularSlider.displacementX, 0.0f);
        float distance = Vector3.Distance(transform.position, strPosition);

        if (distance > radius)
        {
            transform.position -= (distance - radius) * (transform.position - strPosition).normalized;
        }
    }
    public void OnGameFinished(bool inSelfWin)
    {
        if (cueController && !cueController.LoadMainMenuAlreadyCalled)
        {
            // reward self with chips (this happens only when opponent explicitly disconnects from the game)
            if (inSelfWin)
            {
                GameManager_script.Instance().UpdateCoinCount(2.0f * GameManager_script.Instance().CurrentWager);
            }
            else
            {
                GameManager_script.Instance().UpdateCoinCount(0.0f);
            }

            // load main menu for next game
            StartCoroutine(cueController.OnLoadMainMenu(inSelfWin, true)); // will show animations
        }
    }
    IEnumerator PersonalInfoReceived(string inName, int inCue, int inAvatar, int starColor, int starNumber, float TPAScore, int gamesPlayed, int gamesWon, float inCoins, int inStreak)
    {
        while (cueController == null)
        {
            yield return(null);
        }

        yield return(new WaitForEndOfFrame());

        // finally we get to populate info about the other player
        GameManager_script.Instance().PopulateOtherGameProfileInfo(inName, inCue, inAvatar, starColor, starNumber, TPAScore, gamesPlayed, gamesWon, inCoins, inStreak);

        // change player head
        cueController.gamecenter.GetComponent <GameCenter>().ChangeHead2Name();
        cueController.gamecenter.GetComponent <GameCenter>().ChangeHead2Image();

        // change star and other shiites
        cueController.gamecenter.GetComponent <GameCenter>().changeStarImage2(GameManager_script.Instance().otherGameProfileInfo.Star.starType);
        cueController.gamecenter.GetComponent <GameCenter>().changeStarText2("" + GameManager_script.Instance().otherGameProfileInfo.Star.text);
    }
    IEnumerator FriendPersonalInfoReceived(string inName, int inCue, int inAvatar, int starColor, int starNumber, float TPAScore, int gamesPlayed, int gamesWon, float inCoins, int inStreak, float inWager, int inTableTexture)
    {
        while (cueController == null)
        {
            yield return(null);
        }

        yield return(new WaitForEndOfFrame());

        // finally we get to populate info about the other player
        GameManager_script.Instance().PopulateOtherGameProfileInfo(inName, inCue, inAvatar, starColor, starNumber, TPAScore, gamesPlayed, gamesWon, inCoins, inStreak);

        // change player head
        cueController.gamecenter.GetComponent <GameCenter>().ChangeHead2Name();
        cueController.gamecenter.GetComponent <GameCenter>().ChangeHead2Image();

        // change star and other shiites
        cueController.gamecenter.GetComponent <GameCenter>().changeStarImage2(GameManager_script.Instance().otherGameProfileInfo.Star.starType);
        cueController.gamecenter.GetComponent <GameCenter>().changeStarText2("" + GameManager_script.Instance().otherGameProfileInfo.Star.text);

        // see if we need to change up some shit
        if (GameManager_script.Instance().TableTextureIndex > inTableTexture)
        {
            // change table color
            GameReplaceTableTexture.SingleTon().SetTableTexture(inTableTexture);

            // refund excess wager
            GameManager_script.Instance().UpdateCoinCount(GameManager_script.Instance().CurrentWager - inWager);

            // reset current wager
            GameManager_script.Instance().CurrentWager      = (float)inWager;
            GameManager_script.Instance().CurrentWagerLevel = (float)inTableTexture;

            // reset center label
            cueController.gamecenter.GetComponent <GameCenter>().ChangeCenterLabel("Red", GameManager_script.convertNumberIntoGoodStringFormat(GameManager_script.Instance().CurrentWager * 2.0f, "gamecoinz"));
        }
    }
 // Start is called before the first frame update
 void Start()
 {
     gameManager_script = GameObject.Find("GameManager").GetComponent <GameManager_script>();
     canvas             = this.gameObject.GetComponentInChildren <Canvas>();
 }
Beispiel #16
0
    // this function is doing awefully little, only works some sounds and work with the main ball
    // in here, we use reserve velocity all we want but NEVER set it
    void OnCollisionEnter(Collision collision)
    {
        // dajiang physics 2
        // we need angular velocity and real velocity for both items before.
        // we need frictions of the 2 contact surfaces (this is already known through the layerName).

        // reserveVelocity stuff is the backup, which I will NOT change in this function but only change in fixedupdate

        // we need to make sure the exit velocities of the 2 balls are perpendicular to each other
        // you can and do have an initial angular velocity, and in some cases it immediately matches with your real velocity

        // we can set a flag so we know if the real rigidbody.velocity is modified upon impact or not at each frame
        // if it has not been impacted, we will swap in the new values
        // if it has been impacted, we will apply the new value on top of it

        // velocity of the rigidbody right now is already calculated by unity for after the collision
        // if we want to update it any further (or in case of wall, not updated), we can do our calculation here.

        // angular is immediately reconciled for wall collisions and should be reconciles quickly for normal hits except for skidding (which can last some time).

        string layerName = LayerMask.LayerToName(collision.collider.gameObject.layer);

        // An important pre-condition
        // If we get a stationary ball to ball or ball to wall collision due to initial game setting, we need to ignore those collisions because they do crazy things
        if ((layerName == "Wall" || layerName == "Ball" || layerName == "MainBall") && collision.relativeVelocity.magnitude > 0.01f)
        {
            if (layerName == "Ball" || layerName == "MainBall") // ballllzzz
            {
                BallController otherBall = collision.collider.gameObject.GetComponent <BallController>();

                if (otherBall && otherBall.id < id)
                {
                    // sound portion for ball on ball
                    float b_b_volume = Mathf.Clamp01(collision.relativeVelocity.magnitude / cueController.ballMaxVelocity);
                    int   b_b_index  = 0;

                    if (b_b_volume > 0.66f)
                    {
                        b_b_index = Random.Range((int)MusicClip.B_B_Hard_0, (int)MusicClip.B_B_Hard_2 + 1);
                    }
                    else if (b_b_volume > 0.33f)
                    {
                        b_b_index = Random.Range((int)MusicClip.B_B_Mid_0, (int)MusicClip.B_B_Mid_1 + 1);
                    }
                    else
                    {
                        b_b_index = Random.Range((int)MusicClip.B_B_Weak_0, (int)MusicClip.B_B_Weak_1 + 1);
                    }

                    GameManager_script.Instance().PlaySound(b_b_index, false, b_b_volume);
                }

                // real physics stuff starts here
                if (inMove && cueController && collision.collider.GetComponent <Rigidbody>())
                {
                    Vector3 exitVelocity   = Vector3.zero;
                    float   energyRetained = 0.0f;

                    // object ball with a spline attached (risky but necessary for a successful game)
                    // has to be the first contact
                    if (otherBall.isMain && cueController.CurrentBallID == id && cueController.TargetPocketDirection != Vector3.zero)
                    {
                        // assign collision normal
                        exitVelocity = cueController.TargetPocketDirection;

                        // zero out stuff so there is no next use
                        cueController.TargetPocketDirection = Vector3.zero;
                        cueController.CurrentBallID         = -1;

                        // calculate energy retained
                        energyRetained = Vector3.Dot((cueController.currentHitBallController.reserveVelocity + cueController.cueBallController.reserveVelocity).normalized, exitVelocity.normalized);

                        //  the following are all copied from ball controller, make them into a function
                        if (energyRetained < 0.0f)
                        {
                            energyRetained += 1;
                        }

                        if (!cueController.currentHitBallController.GetComponent <Rigidbody>().isKinematic)
                        {
                            cueController.currentHitBallController.GetComponent <Rigidbody>().velocity = exitVelocity.normalized * energyRetained * (cueController.cueBallController.reserveVelocity.magnitude + cueController.currentHitBallController.reserveVelocity.magnitude);
                        }
                    }
                    // cue ball with its spline, this will always check out
                    else if (isMain && cueController.CurrentBallID == otherBall.id && cueController.CueBallBounceDirection != Vector3.zero)
                    {
                        // assign collision normal
                        exitVelocity = cueController.CueBallBounceDirection;

                        // zero out the spline
                        cueController.CueBallBounceDirection = Vector3.zero;

                        // calculate energy retained
                        energyRetained = Vector3.Dot((cueController.currentHitBallController.reserveVelocity + cueController.cueBallController.reserveVelocity).normalized, exitVelocity.normalized);

                        //  the following are all copied from ball controller, make them into a function
                        if (energyRetained < 0.0f)
                        {
                            energyRetained += 1;
                        }

                        if (!cueController.cueBallController.GetComponent <Rigidbody>().isKinematic)
                        {
                            cueController.cueBallController.GetComponent <Rigidbody>().velocity = exitVelocity.normalized * energyRetained * (cueController.cueBallController.reserveVelocity.magnitude + cueController.currentHitBallController.reserveVelocity.magnitude);
                        }
                    }
                    // all other cases of ball to ball collision
                    else
                    {
                        // we use unity built in engine for this purpose
                        exitVelocity = GetComponent <Rigidbody>().velocity;

                        // zero out stuff so there is no next use
                        cueController.TargetPocketDirection  = Vector3.zero;
                        cueController.CurrentBallID          = -1;
                        cueController.CueBallBounceDirection = Vector3.zero;

                        // calculate energy retained
                        energyRetained = exitVelocity.magnitude / (reserveVelocity.magnitude + otherBall.reserveVelocity.magnitude);

                        //  guard against negative numbers
                        if (energyRetained < 0.0f)
                        {
                            energyRetained += 1;
                        }

                        if (!GetComponent <Rigidbody>().isKinematic)
                        {
                            GetComponent <Rigidbody>().velocity = exitVelocity.normalized * energyRetained * (reserveVelocity.magnitude + otherBall.reserveVelocity.magnitude);
                        }
                    }
                }

                // some game rules, you can only first hit the current first ball
                if (cueController.firstBallBallCollisionSinceShot && otherBall != null)
                {
                    if (isMain || otherBall.isMain)
                    {
                        int tempId = 0;

                        if (isMain)
                        {
                            tempId = otherBall.id;
                        }
                        else
                        {
                            tempId = id;
                        }

                        if (cueController.currentBallControllers.Count > 1)
                        {
                            if (tempId != cueController.currentBallControllers[1].id) // first ball remaining
                            {
                                cueController.hittingTheRightFirstBall = false;
                            }
                            else
                            {
                                cueController.hittingTheRightFirstBall = true;
                            }
                        }

                        cueController.firstBallBallCollisionSinceShot = false;
                    }
                }

                cueController.contactedAtLeastOneRealBall = true;
            }

            if (layerName == "Wall" && !GetComponent <Rigidbody>().isKinematic&& !ballIsPocketed) // walllllz, should include pocket walls
            {
                // choose sound
                float b_w_volume = Mathf.Clamp01(collision.relativeVelocity.magnitude / cueController.ballMaxVelocity);
                int   b_w_index  = Random.Range((int)MusicClip.B_W_0, (int)MusicClip.B_W_1 + 1);

                // play sound
                GameManager_script.Instance().PlaySound(b_w_index, false, b_w_volume);

                // take half a ball back to avoid the risk of running into the other side of the wall before our wall, vectoroperator has things that does this as well
                Ray        ray = new Ray(GetComponent <Rigidbody>().position - cueController.ballRadius * reserveVelocity.normalized * 1.25f, reserveVelocity.normalized);
                RaycastHit hit;

                // use a common angular velocity bullshit
                Vector3 reserveXZVelocity        = VectorOperator.getProjectXZ(reserveVelocity, false);
                Vector3 reserveXZAngularVelocity = VectorOperator.getProjectXZ(reserveAngularVelocity, false);

                if (Physics.SphereCast(ray, cueController.ballRadius, out hit, 20.0f * cueController.ballRadius, cueController.wallMask))
                {
                    // make sure we only have a bounce but no pocket direction, meaning it is not a ball hit
                    if (isMain && cueController.CueBallBounceDirection != Vector3.zero && cueController.TargetPocketDirection == Vector3.zero)
                    {
                        // set the straight velocity up
                        GetComponent <Rigidbody>().velocity = Vector3.Magnitude(reserveVelocity) * Vector3.Normalize(cueController.CueBallBounceDirection);

                        // reset everything just to be sure
                        cueController.TargetPocketDirection  = Vector3.zero;
                        cueController.CurrentBallID          = -1;
                        cueController.CueBallBounceDirection = Vector3.zero;
                    }
                    else
                    {
                        // calculate linear velocity
                        GetComponent <Rigidbody>().velocity = reserveXZVelocity - 2.0f * Vector3.Project(reserveXZVelocity, VectorOperator.CleanYAxis(-hit.normal));
                        GetComponent <Rigidbody>().velocity = VectorOperator.getProjectXZ(GetComponent <Rigidbody>().velocity, true);
                    }

                    if (true)
                    {
                        // calculate angular velocity
                        Vector3 XZHitNormal = VectorOperator.CleanYAxis(-hit.normal);

                        // this declaration is f*****g retarded
                        float XAngular = reserveXZAngularVelocity.x;
                        float ZAngular = reserveXZAngularVelocity.z;

                        // z is NOT flat, so x needs to be re-calculated. Since reserve velocity x is always negative of the reserve angular z, so we add.
                        if (Mathf.Abs(XZHitNormal.x) > 0.01f)
                        {
                            ZAngular += Mathf.Abs(XZHitNormal.x) * reserveVelocity.x * 2.0f * 2.0f;                                                         // 2.0f for twice the angular, 2.0f for negation
                            ZAngular  = Mathf.Clamp(ZAngular, Mathf.Abs(reserveXZAngularVelocity.z) * -1.0f, Mathf.Abs(reserveXZAngularVelocity.z) * 1.0f); // dajiang hack, i omitted the other component, make sure ZAngular doesn't exceed the total magnitude it previously had
                        }

                        // x is NOT flat, so z needs to be re-calculated. Since reserve velocity z is always in sync with reserve angluar x, so we subtract.
                        if (Mathf.Abs(XZHitNormal.z) > 0.01f)
                        {
                            XAngular -= Mathf.Abs(XZHitNormal.z) * reserveVelocity.z * 2.0f * 2.0f;                                                         // 2.0f for twice the angular, 2.0f for negation
                            XAngular  = Mathf.Clamp(ZAngular, Mathf.Abs(reserveXZAngularVelocity.x) * -1.0f, Mathf.Abs(reserveXZAngularVelocity.x) * 1.0f); // dajiang hack, i omitted the other component, make sure XAngular doesn't exceed the total magnitude it previously had
                        }

                        GetComponent <Rigidbody>().angularVelocity = new Vector3(XAngular, 0.0f, ZAngular);
                    }

                    // add Y component of angular velocity to linear velocity and subtract itself from the angular component, dajiang hack? Donno what this is no more
                    Vector3 YAngularVelocityOnly = new Vector3(0.0f, reserveAngularVelocity.y, 0.0f);

                    // actually add and subtract angular velocities
                    GetComponent <Rigidbody>().velocity        += 0.36f * Vector3.Magnitude(YAngularVelocityOnly * 0.5f) * Vector3.Cross(VectorOperator.CleanYAxis(-hit.normal), YAngularVelocityOnly.normalized);
                    GetComponent <Rigidbody>().angularVelocity -= new Vector3(0.0f, 0.36f * reserveAngularVelocity.y, 0.0f); // this line checks out

                    // calculate restitution cooef, 1 is straight, 0 is slice
                    float impactAngle = Mathf.Clamp01(Vector3.Dot(reserveXZVelocity.normalized, VectorOperator.CleanYAxis(-hit.normal).normalized));

                    // restitution, choose wisely
                    GetComponent <Rigidbody>().velocity        = (0.96f - impactAngle * 0.24f) * GetComponent <Rigidbody>().velocity;
                    GetComponent <Rigidbody>().angularVelocity = (0.96f - impactAngle * 0.24f) * GetComponent <Rigidbody>().angularVelocity;
                }

                // only start counting the wall hits after a good ball hit, otherwise all wall hits are pointless
                if (cueController.contactedAtLeastOneRealBall)
                {
                    cueController.wallHitCount += 1;
                }
            }
        }
    }
Beispiel #17
0
    // in here, we only set the reserve velocity at the very end and NEVER use it in the middle
    void FixedUpdate()
    {
        if (GameManager_script.Instance().SmartBotFreezeInPlaceFlag)
        {
            // save k state
            bool kinematicState = GetComponent <Rigidbody>().isKinematic;

            // give it a new state
            GetComponent <Rigidbody>().isKinematic = false;

            // freeze all ball in their positions
            GetComponent <Rigidbody>().position = GetComponent <Rigidbody>().position;

            // freeze all ball's velocities
            GetComponent <Rigidbody>().velocity = Vector3.zero;

            // freeze all ball's angular velocities
            GetComponent <Rigidbody>().angularVelocity = Vector3.zero;

            // put the state back
            GetComponent <Rigidbody>().isKinematic = kinematicState;
        }
        else
        {
            // dajiang physics 1
            // we need angular velocity and real velocity, since we are working with perfect surfaces, we can go ahead and update it ourselves.
            // we need frictions of the surface (which reacts differently to different angular and direct speeds)
            if (cueController && cueController.ballsIsCreated)
            {
                if (ballIsPocketed)
                {
                    if (!GetComponent <Rigidbody>().isKinematic&& initialAnimationPlacement < initialHoleSplineLength)
                    {
                        // 20.0f is the constant speed where balls drop
                        initialHoleSpline.AnimationSlider(transform, 17.5f, ref initialAnimationPlacement, out ballVeolociyInHole, 1, false);

                        // special casing for 3D ball rolling, we just want to apply this to the final section of the roll
                        if (initialAnimationPlacement / initialHoleSplineLength > 0.5f)
                        {
                            GetComponent <Rigidbody>().position += cueController.ThreeDOffset;
                        }

                        GetComponent <Rigidbody>().velocity        = ballVeolociyInHole /*球在洞里的速度*/;
                        GetComponent <Rigidbody>().angularVelocity = new Vector3(ballVeolociyInHole.z * 2.0f, ballVeolociyInHole.y * 2.0f, -ballVeolociyInHole.x * 2.0f);
                    }
                    else if (!GetComponent <Rigidbody>().isKinematic&& finalAnimationPlacement < finalHoleSplineLength)
                    {
                        // 20.0f is the constant speed where balls drop
                        finalHoleSpline.AnimationSlider(transform, 17.5f, ref finalAnimationPlacement, out ballVeolociyInHole, 1, false);

                        // special casing for 3D ball rolling
                        GetComponent <Rigidbody>().position += cueController.ThreeDOffset;

                        GetComponent <Rigidbody>().velocity        = ballVeolociyInHole;
                        GetComponent <Rigidbody>().angularVelocity = new Vector3(ballVeolociyInHole.z * 2.0f, ballVeolociyInHole.y * 2.0f, -ballVeolociyInHole.x * 2.0f);
                    }
                    else
                    {
                        if (GetComponent <Collider>().enabled)
                        {
                            BallPocketedHouseKeeping(finalHoleSplineLength);
                        }
                    }
                }
                else
                {
                    if (inMove && !GetComponent <Rigidbody>().isKinematic) // ball is moving
                    {
                        // do velocity and angular velocity sync check
                        Vector3 noyVelocity        = new Vector3(GetComponent <Rigidbody>().velocity.x, 0, GetComponent <Rigidbody>().velocity.z);
                        Vector3 noyAngularVelocity = new Vector3(-0.5f * GetComponent <Rigidbody>().angularVelocity.z, 0, 0.5f * GetComponent <Rigidbody>().angularVelocity.x);

                        // different scores and hard gates
                        float VelocityMultiple  = 0.0f; // from -1.0f to 1.0f, from completely opposite to completely synced
                        float VelocityMagnitude = 0.0f; // from 0.0f to 1.5f, from completely stationary to completely top speed

                        // 1 is when we are either completely in sync or completely opposite
                        // 0 is when one speed is 0 and the other speed is not
                        if (noyVelocity.magnitude != 0.0f || noyAngularVelocity.magnitude != 0.0f)
                        {
                            //会返回区间在0~1之间的value
                            if (noyVelocity.magnitude > noyAngularVelocity.magnitude)
                            {
                                VelocityMultiple = Mathf.Clamp01(noyAngularVelocity.magnitude / noyVelocity.magnitude);
                            }
                            else
                            {
                                VelocityMultiple = Mathf.Clamp01(noyVelocity.magnitude / noyAngularVelocity.magnitude);
                            }
                        }

                        // 2 speeds are countering each other, so we subtract  2个速度相互抵触,所以我们减去
                        if (Vector3.Dot(noyVelocity.normalized, noyAngularVelocity.normalized) < 0.0f)
                        {
                            VelocityMultiple = VelocityMultiple - 1.0f;
                        }

                        // clamp it
                        VelocityMultiple = Mathf.Clamp(VelocityMultiple, -1.0f, 1.0f);

                        // 1.5f when we spin at 71%. 1.3f when we completely spin to 1 side. 1.0f is when we shoot straight
                        VelocityMagnitude = Mathf.Clamp((noyVelocity.magnitude + noyAngularVelocity.magnitude) / cueController.ballMaxVelocity, 0.0f, 1.5f);

                        // we need to reconcile somethingz
                        if ((noyVelocity - noyAngularVelocity).magnitude > 0.1f /* 0.1f*/)
                        {
                            float reconciliationE = 0.0500f;                             //0.0500f// between 500 and 600

                            // reconcile more when speed is higher, till 1.2ish, then slightly decline coz the ball is plating but skidding anymore
                            reconciliationE = reconciliationE * cueController.ReconVelocityMagnitudeCurve.Evaluate(VelocityMagnitude);

                            // reconcile more when multiple is closer to -1.0f  多数更接近-1.0f时,调和更多
                            reconciliationE = reconciliationE * cueController.ReconVelocityMultipleCurve.Evaluate(VelocityMagnitude);

                            // preparations  准备
                            Vector3 velocityDifference = noyVelocity - noyAngularVelocity;

                            // actual reconciliation 实际和解
                            noyVelocity -= velocityDifference.normalized * reconciliationE;
                            //20170712
                            noyAngularVelocity += velocityDifference.normalized * 2.5f /*2.5f*/ * reconciliationE;                            // dajiang hack, should be 2.5f, 100 trans turns into 71.5 trans and 143 ang. This means ang is 143/28.5 == 5.0 of trans, thus 2.5f since we divided it by 2.0 before
                        }

                        // we need to decay somethingz
                        if (true)
                        {
                            float decayE = 0.000100f;//0.000100f;

                            // decay more when speed is closer to 0.0f
                            decayE = decayE * cueController.DecayVelocityMagnitudeCurve.Evaluate(VelocityMagnitude);

                            // decay more when multiple is closer to -1.0f
                            decayE = decayE * cueController.DecayVelocityMultipleCurve.Evaluate(VelocityMagnitude);

                            // actual decay   //Gu,控制球的撞击后滚动的速度,(那个困扰了几个月的BUG)
                            GetComponent <Rigidbody>().velocity        = this.transform.GetComponent <Rigidbody>().velocity * 0.9987f;//new Vector3(noyVelocity.x * (1.0f - decayE), GetComponent<Rigidbody>().velocity.y * (1.0f - decayE), noyVelocity.z * (1.0f - decayE));
                            GetComponent <Rigidbody>().angularVelocity = new Vector3(2.0f * noyAngularVelocity.z * (1.0f - decayE), GetComponent <Rigidbody>().angularVelocity.y *(1.0f - decayE), -2.0f * noyAngularVelocity.x * (1.0f - decayE));
                        }
                    }
                }

                // we will always do this thing no matter what? ya we do.
                if (GetComponent <Rigidbody>().velocity.magnitude < suddenStop && GetComponent <Rigidbody>().angularVelocity.magnitude < suddenStop * 2.0f) // this number needs to be greater than sleep threshold
                {
                    if (!GetComponent <Rigidbody>().isKinematic)
                    {
                        // dajiang hack, all these numbers depend on the table NEVER gets moved. So we really shouldn't be moving the table under any circumstances.
                        xClamp = GetComponent <Rigidbody>().position.z > 6.09f || GetComponent <Rigidbody>().position.z < -11.30f ? GetComponent <Rigidbody>().position.x : Mathf.Clamp(GetComponent <Rigidbody>().position.x, -18.71f, 19.05f);
                        zClamp = GetComponent <Rigidbody>().position.x > 18.60f || GetComponent <Rigidbody>().position.x < -18.26f || (GetComponent <Rigidbody>().position.x > -0.81f && GetComponent <Rigidbody>().position.x < 1.15f) ? GetComponent <Rigidbody>().position.z : Mathf.Clamp(GetComponent <Rigidbody>().position.z, -11.73f, 6.54f);

                        GetComponent <Rigidbody>().position        = new Vector3(xClamp, GetComponent <Rigidbody>().position.y, zClamp);
                        GetComponent <Rigidbody>().velocity        = Vector3.zero;
                        GetComponent <Rigidbody>().angularVelocity = Vector3.zero;
                    }
                }

                // put it in for next time a collision or something weird happens
                reserveVelocity        = GetComponent <Rigidbody>().velocity;
                reserveAngularVelocity = GetComponent <Rigidbody>().angularVelocity;
            }
        }
    }
    void Awake()
    {
        gameMng = GameManager_script.ManagerInstance;
        gameMng.ThrowDices();
        diceImages = new Dictionary<int, Texture2D>();

        diceImages.Add(1, Resources.Load("Dice1") as Texture2D);
        diceImages.Add(2, Resources.Load("Dice2") as Texture2D);
        diceImages.Add(3, Resources.Load("Dice3") as Texture2D);
        diceImages.Add(4, Resources.Load("Dice4") as Texture2D);
        diceImages.Add(5, Resources.Load("Dice5") as Texture2D);
        diceImages.Add(6, Resources.Load("Dice6") as Texture2D);
        diceImages.Add(7, Resources.Load("Scored_1") as Texture2D);
        RollDices();
    }
    IEnumerator StatusUpdateStillPositions(int inShotCount, bool inPlayerInControl, bool inPushoutable, bool inSkippable, int inStatus, bool[] inPocketed, Vector3[] inPositions, bool inFirstTime)
    {
        while (cueController == null)
        {
            yield return(null);
        }

        yield return(new WaitForEndOfFrame());

        bool ControlReallyChanged = ServerController.serverController.playerInControl != inPlayerInControl;

        ServerController.serverController.playerInControl = inPlayerInControl;

        if (ControlReallyChanged && !inFirstTime)
        {
            float transfer_volume = Mathf.Clamp01(1.0f);

            if (ServerController.serverController.playerInControl)
            {
                GameManager_script.Instance().PlaySound((int)MusicClip.Good_Transfer, false, transfer_volume); // getting control always cheers
            }
            else
            {
                GameManager_script.Instance().PlaySound((int)MusicClip.Bad_Transfer, false, transfer_volume); // losing control always sad
            }
        }

        CueControllerUpdater.current_control_status = inStatus;

        cueController.pushoutAllowed = inPushoutable;
        cueController.skipAllowed    = inSkippable;

        cueController.stillBallPositions = inPositions;
        cueController.stillBallPocketed  = inPocketed;

        StartCoroutine(UpdateStillPositions(inShotCount));

        cueController.ConditionalEnableForceSlider(); // update force slider control

        // activate hand cursor
        if (CueControllerUpdater.current_control_status == CueControllerUpdater.CUE_BALL_MOVABLE_ON_TABLE && inPlayerInControl && !inFirstTime)
        {
            cueController.ActivateHandCursor();
        }

        // increment balls missed (when we transfer control over to player while it being on table)
        if (CueControllerUpdater.current_control_status == CueControllerUpdater.CUE_BALL_FIXED_ON_TABLE && inPlayerInControl && !inSkippable)
        {
            GameManager_script.IncrementShotsMissed(cueController.TrackingShotAsPlayerOne, cueController.TrackingShotAsPlayerTwo);
        }

        // show breakz pushout
        if (inFirstTime)
        {
            cueController.ShowHelpfulTooltipPopup("", "YourBreak", true, false);

            if (cueController.BotInControl() || cueController.NetworkSlaveInControl() || cueController.NetworkBotInControl()) // set for rematch breaks
            {
                GameManager_script.Instance().rematchYouAreThePrevBreaker = false;
            }
            else
            {
                GameManager_script.Instance().rematchYouAreThePrevBreaker = true;
            }
        }
    }
    public void OnRematchConfirmationReceived(bool inWantToRematch)
    {
        if (inWantToRematch && GameManager_script.Instance().CoinCount >= GameManager_script.Instance().CurrentWager)
        {
            GameManager_script.Instance().rematchOppoWantToRematch = true;
            GameManager_script.Instance().rematchSmartBotSeries    = GameManager_script.Instance().SmartBotInActionGame;

            if (GameManager_script.Instance().rematchSelfWantToRematch)
            {
                // it is time to hide the room for all other players forever
                GameManager_script.KillRoomAndDisconnect();

                // for photon view bug?
                GameManager_script.DestroyServerController();

                GameManager_script.Instance().rematchCurrentMatchIsRematch = true;
                GameManager_script.Instance().rematchSelfWantToRematch     = false;
                GameManager_script.Instance().rematchOppoWantToRematch     = false;

                GameManager_script.Instance().StartingOutAsANetWorkGame = true;
                GameManager_script.Instance().CurrentlyInANetWorkGame   = true;
                GameManager_script.Instance().SmartBotInActionGame      = false;
                GameManager_script.Instance().StupidBotInActionGame     = false;
                GameManager_script.Instance().TrulySelfInActionGame     = false;
                GameManager_script.Instance().FTUEInActionGame          = false;

                GameManager_script.Instance().TableTextureIndex = (int)GameManager_script.Instance().CurrentWagerLevel;

                GameManager_script.Instance().PopulateSelfGameProfileInfo();

                GameManager_script.Instance().PopulateInterstitialStartScreen
                (
                    true,
                    GameManager_script.Instance().GetMaxTPAScore(),
                    GameManager_script.Instance().Total_Games_Played,
                    GameManager_script.Instance().Total_Games_Won,
                    GameManager_script.Instance().CoinCount - GameManager_script.Instance().CurrentWager,
                    GameManager_script.Instance().Current_Win_Streak
                );

                GameManager_script.Instance().resetSingleGameStats();

                GameManager_script.Instance().StartCoroutine(GameManager_script.Instance().PlayLookingForPlayerMusic());

                GameManager_script.Instance().PopupCurrentlyVisible = false;

                GameManager_script.Instance().NetworkGameSceneCurrentLoad = true;

                Application.LoadLevel("GameStart");
            }
            else
            {
                // display to self that opponent wants to rematch!
                GameManager_script.Instance().ChangeRematchToolTipAbsolutePath(true, "RematchYes");
            }
        }
        else
        {
            GameManager_script.Instance().rematchOppoWantToRematch = false;

            // show self that the other person don't want to rematch (only when we are waiting)
            GameManager_script.Instance().ChangeRematchToolTipAbsolutePath(true, "RematchNo");
        }
    }
 //sets the instance to null when the application quits
 public void OnApplicationQuit()
 {
     managerInstance = null;
 }