IEnumerator UpdateCharacterVisuals()
    {
        yield return(null);

        IHueShiftableVisuals hueShift = registration.playgroundAvatarVisuals;

        if (hueShift == null)
        {
            gameVisualsRoutine = null;
        }
        else
        {
            Vector2 currentlyDisplayedVector = characterVisualsVector;
            Vector2 previousStoredVector     = characterVisualsVector;

            while (true)
            {
                yield return(null);

                currentlyDisplayedVector = characterVisualsVector;
                hueShift.shiftAsync      = characterVisualsVector;
            }
        }
    }
    // Use this for initialization
    void Awake()
    {
        Assert.IsTrue(Time.timeScale == 1);
        Assert.IsTrue(MenuMusic.singleton == null);
        if (Time.timeScale != 1)
        {
            Pause.unPause();
        }

        if (SetupData.self != null) //workaround for unity level-loading method order
        {
            data = SetupData.self;
        }
        else
        {
            Debug.Log("Setup data not linked");
        }

        Observers.Subscribe(this, GoalScoredMessage.classMessageType, GameEndMessage.classMessageType, OvertimeMessage.classMessageType);

        leftPoints = new Vector2[leftRespawnPointsParent.childCount];
        int index = 0;

        foreach (Transform child in leftRespawnPointsParent)
        {
            leftPoints[index] = child.position;
            index++;
        }

        index       = 0;
        rightPoints = new Vector2[rightRespawnPointsParent.childCount];
        foreach (Transform child in rightRespawnPointsParent)
        {
            rightPoints[index] = child.position;
            index++;
        }

        puck = ((GameObject)(Instantiate(PuckPrefab, puckRespawnPoint.position, Quaternion.identity))).GetComponent <PuckFX>();
        puck.gameObject.AddComponent <OutOfBoundsCheck>().Init(new Vector2(-horizontalBounds - 1, -verticalBounds), new Vector2(horizontalBounds + 1, verticalBounds));

        players = new Transform[data.playerComponentPrefabs.Length];
        for (int i = 0; i < data.playerComponentPrefabs.Length; i++)
        {
            GameObject spawnedPlayer = (GameObject)Instantiate(data.playerComponentPrefabs[i].character.basePlayer, new Vector2((i + 1) * 200, 0), Quaternion.identity); //the positions are temporary
            Stats      spawnedStats  = spawnedPlayer.AddComponent <Stats>();
            spawnedStats.side        = data.playerComponentPrefabs[i].character.side;
            spawnedStats.playerID    = data.playerComponentPrefabs[i].playerID;
            spawnedStats.networkMode = data.playerComponentPrefabs[i].bindings.networkMode;

            switch (data.playerComponentPrefabs[i].bindings.inputMode)
            {
            case InputConfiguration.PlayerInputType.MOUSE:
                switch (data.playerComponentPrefabs[i].bindings.networkMode)
                {
                case NetworkMode.LOCALCLIENT:
                case NetworkMode.LOCALSERVER:
                case NetworkMode.UNKNOWN:
                    spawnedPlayer.AddComponent <MousePlayerInput>().bindings = data.playerComponentPrefabs[i].bindings;
                    break;
                }
                break;

            case InputConfiguration.PlayerInputType.JOYSTICK:
                switch (data.playerComponentPrefabs[i].bindings.networkMode)
                {
                case NetworkMode.LOCALCLIENT:
                case NetworkMode.LOCALSERVER:
                case NetworkMode.UNKNOWN:
                    spawnedPlayer.AddComponent <JoystickCustomDeadZoneInput>().bindings = data.playerComponentPrefabs[i].bindings;
                    break;
                }
                break;

            case InputConfiguration.PlayerInputType.CRAPAI:
                spawnedPlayer.AddComponent <CrappyAIInput>().bindings = data.playerComponentPrefabs[i].bindings;    //don't really need the bindings; it's an AI
                break;

            case InputConfiguration.PlayerInputType.INTERPOSEAI:
                spawnedPlayer.AddComponent <InterposeAI>().bindings = data.playerComponentPrefabs[i].bindings;    //don't really need the bindings; it's an AI
                break;

            case InputConfiguration.PlayerInputType.DEFENSIVEAI:
                spawnedPlayer.AddComponent <DefensiveAI>().bindings = data.playerComponentPrefabs[i].bindings;    //don't really need the bindings; it's an AI
                break;
            }
            GameObject.Instantiate(data.playerComponentPrefabs[i].character.movementAbility).transform.SetParent(spawnedPlayer.transform, false);
            GameObject.Instantiate(data.playerComponentPrefabs[i].character.genericAbility).transform.SetParent(spawnedPlayer.transform, false);
            GameObject.Instantiate(data.playerComponentPrefabs[i].character.superAbility).transform.SetParent(spawnedPlayer.transform, false);
            GameObject visuals = GameObject.Instantiate(data.playerComponentPrefabs[i].character.visuals);
            visuals.transform.SetParent(spawnedPlayer.transform, false);
            IHueShiftableVisuals huedVisuals = visuals.GetComponent <IHueShiftableVisuals>();
            if (huedVisuals != null)
            {
                huedVisuals.shift = data.playerComponentPrefabs[i].characterVisualsVector;
            }
            players[i] = spawnedPlayer.transform;
            switch (data.playerComponentPrefabs[i].character.side)
            {
            case Side.LEFT:
                leftPlayers.Add(players[i]);
                break;

            case Side.RIGHT:
                rightPlayers.Add(players[i]);
                break;
            }
        }
        //set up AI/Ability data
        Goal[] goals = GameObject.FindObjectsOfType <Goal>();
        switch (goals[0].side)
        {
        case Side.LEFT:
            leftGoal  = goals[0].transform;
            rightGoal = goals[1].transform;
            break;

        case Side.RIGHT:
            rightGoal = goals[0].transform;
            leftGoal  = goals[1].transform;
            break;
        }
        for (int i = 0; i < players.Length; i++)
        {
            AbstractPlayerInput input = players[i].GetComponent <AbstractPlayerInput>();
            if (input is IInterferenceAI)
            {
                switch (data.playerComponentPrefabs[i].character.side)
                {
                case Side.LEFT:
                    (input as IInterferenceAI).myOpponents = rightPlayers;
                    break;

                case Side.RIGHT:
                    (input as IInterferenceAI).myOpponents = leftPlayers;
                    break;
                }
            }

            if (input is IGoalAI)
            {
                switch (data.playerComponentPrefabs[i].character.side)
                {
                case Side.LEFT:
                    (input as IGoalAI).myGoal       = leftGoal;
                    (input as IGoalAI).opponentGoal = rightGoal;
                    break;

                case Side.RIGHT:
                    (input as IGoalAI).myGoal       = rightGoal;
                    (input as IGoalAI).opponentGoal = leftGoal;
                    break;
                }
            }

            //now abilities

            foreach (AbstractAbility ability in players[i].GetComponentsInChildren <AbstractAbility>())
            {
                if (ability is IOpponentsAbility)
                {
                    switch (data.playerComponentPrefabs[i].character.side)
                    {
                    case Side.LEFT:
                        (ability as IOpponentsAbility).opponents = rightPlayers;
                        break;

                    case Side.RIGHT:
                        (ability as IOpponentsAbility).opponents = leftPlayers;
                        break;
                    }
                }
                if (ability is IAlliesAbility)
                {
                    switch (data.playerComponentPrefabs[i].character.side)
                    {
                    case Side.LEFT:
                        (ability as IAlliesAbility).allies = leftPlayers;
                        break;

                    case Side.RIGHT:
                        (ability as IAlliesAbility).allies = rightPlayers;
                        break;
                    }
                }
                if (ability is IGoalAbility)
                {
                    switch (data.playerComponentPrefabs[i].character.side)
                    {
                    case Side.LEFT:
                        (ability as IGoalAbility).myGoal       = leftGoal;
                        (ability as IGoalAbility).opponentGoal = rightGoal;
                        break;

                    case Side.RIGHT:
                        (ability as IGoalAbility).myGoal       = rightGoal;
                        (ability as IGoalAbility).opponentGoal = leftGoal;
                        break;
                    }
                }
                if (ability is IPuckAbility)
                {
                    (ability as IPuckAbility).puck = puck.transform;
                }
            }
        }
    }
    void spawnPlaygroundAvatar(int playerID)
    {
        Registration        data      = registeredPlayers[playerID];
        CharacterComponents character = charactersData[data.SelectedCharacterID].character;

        GameObject spawnedPlayer = (GameObject)Instantiate(character.basePlayer);

        spawnedPlayer.AddComponent <OutOfBoundsCheck>().Init(new Vector2(-horizontalBounds, -verticalBounds), new Vector2(horizontalBounds, verticalBounds));
        Stats spawnedStats = spawnedPlayer.AddComponent <Stats>();

        spawnedStats.side        = character.side;
        spawnedStats.playerID    = playerID;
        spawnedStats.networkMode = data.networkMode;

        AbstractPlayerInput input;

        switch (data.networkMode)
        {
        case NetworkMode.REMOTECLIENT:
        case NetworkMode.REMOTESERVER:
            break;

        default:
            if (data.localID != -1)
            {
                switch (possiblePlayers[data.localID].bindings.inputMode)
                {
                case InputConfiguration.PlayerInputType.MOUSE:
                    input          = spawnedPlayer.AddComponent <MousePlayerInput>();
                    input.bindings = possiblePlayers[data.localID].bindings;
                    break;

                case InputConfiguration.PlayerInputType.JOYSTICK:
                    input          = spawnedPlayer.AddComponent <JoystickCustomDeadZoneInput>();
                    input.bindings = possiblePlayers[data.localID].bindings;
                    break;
                }
            }
            break;
        }
        GameObject movementAbility = GameObject.Instantiate(character.movementAbility);

        movementAbility.GetComponentInChildren <NotSuperAbility>().cooldownTime /= 4;
        movementAbility.transform.SetParent(spawnedPlayer.transform, false);

        GameObject genericAbility = GameObject.Instantiate(character.genericAbility);

        genericAbility.GetComponentInChildren <NotSuperAbility>().cooldownTime /= 4;
        genericAbility.transform.SetParent(spawnedPlayer.transform, false);

        GameObject.Instantiate(nullSuperPrefab).transform.SetParent(spawnedPlayer.transform, false);

        GameObject instantiatedMeshInteraction = GameObject.Instantiate(meshInteraction);

        instantiatedMeshInteraction.transform.SetParent(spawnedPlayer.transform, false);
        instantiatedMeshInteraction.GetComponent <ParticleSystem>().startColor = data.localID != -1 ? possiblePlayers[data.localID].color : data.color;

        GameObject visuals = GameObject.Instantiate(character.visuals);

        visuals.transform.SetParent(spawnedPlayer.transform, false);
        IHueShiftableVisuals huedVisuals = visuals.GetComponent <IHueShiftableVisuals>();

        if (huedVisuals != null)
        {
            data.playgroundAvatarVisuals = huedVisuals;
            huedVisuals.shiftAsync       = data.ui.CharacterVisualsVector;
        }
        data.playgroundAvatar = spawnedPlayer;

        InputToAction action = spawnedPlayer.GetComponent <InputToAction>();

        action.movementEnabled  = true;
        action.abilitiesEnabled = true;

        switch (character.side)
        {
        case Side.LEFT:
            leftPlayers.Add(spawnedPlayer.transform);
            break;

        case Side.RIGHT:
            rightPlayers.Add(spawnedPlayer.transform);
            break;
        }

        foreach (AbstractAbility ability in spawnedPlayer.GetComponentsInChildren <AbstractAbility>())
        {
            if (ability is IOpponentsAbility)
            {
                switch (character.side)
                {
                case Side.LEFT:
                    (ability as IOpponentsAbility).opponents = rightPlayers;
                    break;

                case Side.RIGHT:
                    (ability as IOpponentsAbility).opponents = leftPlayers;
                    break;
                }
            }
            if (ability is IAlliesAbility)
            {
                switch (character.side)
                {
                case Side.LEFT:
                    (ability as IAlliesAbility).allies = leftPlayers;
                    break;

                case Side.RIGHT:
                    (ability as IAlliesAbility).allies = rightPlayers;
                    break;
                }
            }
            if (ability is IPuckAbility)
            {
                (ability as IPuckAbility).puck = puck.transform;
            }
        }
        //Callback.FireForUpdate(() => spawnedPlayer.GetComponent<ResetScripting>().Reset(echoPosition, 0f), this);
    }