private static LevelWave GetLevelWaveFromWaveSpec(WaveSpecifics waveSpec) {
        var levelNumber = waveSpec.SpawnLevelNumber;
        var waveNumber = waveSpec.SpawnWaveNumber;

        if (LevelSettings.Instance.LevelTimes.Count <= levelNumber) {
            return null;
        }

        var wave = LevelSettings.Instance.LevelTimes[levelNumber].WaveSettings[waveNumber];
        return wave;
    }
Esempio n. 2
0
        public WaveSpecifics Clone()
        {
            var clone = new WaveSpecifics {
                isExpanded             = isExpanded,
                enableWave             = enableWave,
                visualizeWave          = visualizeWave,
                SpawnLevelNumber       = SpawnLevelNumber,
                SpawnWaveNumber        = SpawnWaveNumber,
                MinToSpwn              = MinToSpwn,
                MaxToSpwn              = MaxToSpwn,
                WaveDelaySec           = WaveDelaySec,
                TimeToSpawnEntireWave  = TimeToSpawnEntireWave,
                prefabToSpawn          = prefabToSpawn,
                spawnSource            = spawnSource,
                prefabPoolIndex        = prefabPoolIndex,
                prefabPoolName         = prefabPoolName,
                repeatWaveUntilNew     = repeatWaveUntilNew,
                waveCompletePercentage = waveCompletePercentage,

                curWaveRepeatMode      = curWaveRepeatMode,
                curTimedRepeatWaveMode = curTimedRepeatWaveMode,
                repeatPauseMinimum     = repeatPauseMinimum,
                repeatPauseMaximum     = repeatPauseMaximum,
                repeatItemInc          = repeatItemInc,
                repeatTimeInc          = repeatTimeInc,
                repeatItemLmt          = repeatItemLmt,
                repeatTimeLmt          = repeatTimeLmt,
                repetitionsToDo        = repetitionsToDo,
                repeatPassCriteria     = repeatPassCriteria,

                waveRepeatBonusesEnabled    = waveRepeatBonusesEnabled,
                waveRepeatVariableModifiers = waveRepeatVariableModifiers,
                waveRepeatFireEvents        = waveRepeatFireEvents,
                waveRepeatCustomEvents      = waveRepeatCustomEvents,

                positionExpanded = positionExpanded,
                positionXmode    = positionXmode,
                positionYmode    = positionYmode,
                positionZmode    = positionZmode,
                customPosX       = customPosX,
                customPosY       = customPosY,
                customPosZ       = customPosZ,

                curRotationMode = curRotationMode,
                customRotation  = customRotation,

                enableLimits = enableLimits,
                doNotSpawnIfMbrCloserThan = doNotSpawnIfMbrCloserThan,
                doNotSpawnRandomDist      = doNotSpawnRandomDist,

                enableRandomizations = enableRandomizations,
                randomXRotation      = randomXRotation,
                randomYRotation      = randomYRotation,
                randomZRotation      = randomZRotation,
                randomDistX          = randomDistX,
                randomDistY          = randomDistY,
                randomDistZ          = randomDistZ,

                randomXRotMin = randomXRotMin,
                randomXRotMax = randomXRotMax,
                randomYRotMin = randomYRotMin,
                randomYRotMax = randomYRotMax,
                randomZRotMin = randomZRotMin,
                randomZRotMax = randomZRotMax,

                enableIncrements   = enableIncrements,
                enableKeepCenter   = enableKeepCenter,
                incrementPositionX = incrementPositionX,
                incrementPositionY = incrementPositionY,
                incrementPositionZ = incrementPositionZ,
                incrementRotX      = incrementRotX,
                incrementRotY      = incrementRotY,
                incrementRotZ      = incrementRotZ,

                waveOffsetList = waveOffsetList,

                enablePostSpawnNudge = enablePostSpawnNudge,
                postSpawnNudgeFwd    = postSpawnNudgeFwd,
                postSpawnNudgeRgt    = postSpawnNudgeRgt,
                postSpawnNudgeDwn    = postSpawnNudgeDwn
            };

            return(clone);
        }
    private void AddStatModifier(string modifierName, WaveSpecifics spec) {
        if (spec.repeatPassCriteria.HasKey(modifierName)) {
            DTInspectorUtility.ShowAlert("This wave already has a Variable Limit for World Variable: " + modifierName + ". Please modify the existing one instead.");
            return;
        }

        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "add Variable Limit");

        var myVar = WorldVariableTracker.GetWorldVariableScript(modifierName);

        spec.repeatPassCriteria.statMods.Add(new WorldVariableModifier(modifierName, myVar.varType));
    }
        private void AfterSpawnForVisualization(WaveSpecifics wave, Transform spawnedTrans)
        {
            if (wave.enablePostSpawnNudge) {
                spawnedTrans.Translate(Vector3.forward * wave.postSpawnNudgeFwd.Value);
                spawnedTrans.Translate(Vector3.right * wave.postSpawnNudgeRgt.Value);
                spawnedTrans.Translate(Vector3.down * wave.postSpawnNudgeDwn.Value);
            }

            switch (spawnLayerMode) {
                case SpawnLayerTagMode.UseSpawnerSettings:
                    spawnedTrans.gameObject.layer = gameObject.layer;
                    break;
                case SpawnLayerTagMode.Custom:
                    spawnedTrans.gameObject.layer = spawnCustomLayer;
                    break;
            }

            switch (spawnTagMode) {
                case SpawnLayerTagMode.UseSpawnerSettings:
                    spawnedTrans.gameObject.tag = gameObject.tag;
                    break;
                case SpawnLayerTagMode.Custom:
                    spawnedTrans.gameObject.tag = spawnCustomTag;
                    break;
            }
        }
        private void CheckForValidVariablesForWave(WaveSpecifics wave)
        {
            // examine all KillerInts
            wave.MinToSpwn.LogIfInvalid(_trans, "Min To Spawn", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.MaxToSpwn.LogIfInvalid(_trans, "Max To Spawn", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.repeatItemInc.LogIfInvalid(_trans, "Spawn Increase", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.repeatItemLmt.LogIfInvalid(_trans, "Spawn Limit", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.repetitionsToDo.LogIfInvalid(_trans, "Repetitions", wave.SpawnLevelNumber, wave.SpawnWaveNumber);

            if (wave.positionXmode == WaveSpecifics.PositionMode.CustomPosition) {
                wave.customPosX.LogIfInvalid(_trans, "Custom X Position", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            }
            if (wave.positionYmode == WaveSpecifics.PositionMode.CustomPosition) {
                wave.customPosY.LogIfInvalid(_trans, "Custom Y Position", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            }
            if (wave.positionZmode == WaveSpecifics.PositionMode.CustomPosition) {
                wave.customPosZ.LogIfInvalid(_trans, "Custom Z Position", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            }

            // examine all KillerFloats
            wave.WaveDelaySec.LogIfInvalid(_trans, "Delay Wave (sec)", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.TimeToSpawnEntireWave.LogIfInvalid(_trans, "Time To Spawn All", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.repeatPauseMinimum.LogIfInvalid(_trans, "Repeat Pause Min", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.repeatPauseMaximum.LogIfInvalid(_trans, "Repeat Pause Max", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.repeatTimeInc.LogIfInvalid(_trans, "Repeat Time Increase", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.repeatItemLmt.LogIfInvalid(_trans, "Repeat Time Limit", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.doNotSpawnIfMbrCloserThan.LogIfInvalid(_trans, "Spawn Limit Min. Distance", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.doNotSpawnRandomDist.LogIfInvalid(_trans, "Spawn Limit Random Distance", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.randomDistX.LogIfInvalid(_trans, "Rand. Distance X", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.randomDistY.LogIfInvalid(_trans, "Rand. Distance Y", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.randomDistZ.LogIfInvalid(_trans, "Rand. Distance Z", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.randomXRotMin.LogIfInvalid(_trans, "Rand. X Rot. Min", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.randomXRotMax.LogIfInvalid(_trans, "Rand. X Rot. Max", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.randomYRotMin.LogIfInvalid(_trans, "Rand. Y Rot. Min", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.randomYRotMax.LogIfInvalid(_trans, "Rand. Y Rot. Max", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.randomYRotMin.LogIfInvalid(_trans, "Rand. Z Rot. Min", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.randomYRotMax.LogIfInvalid(_trans, "Rand. Z Rot. Max", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.incrementPositionX.LogIfInvalid(_trans, "Incremental Distance X", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.incrementPositionY.LogIfInvalid(_trans, "Incremental Distance Y", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.incrementPositionZ.LogIfInvalid(_trans, "Incremental Distance Z", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.incrementRotX.LogIfInvalid(_trans, "Incremental Rotation X", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.incrementRotY.LogIfInvalid(_trans, "Incremental Rotation Y", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.incrementRotZ.LogIfInvalid(_trans, "Incremental Rotation Z", wave.SpawnLevelNumber,
                wave.SpawnWaveNumber);
            wave.postSpawnNudgeFwd.LogIfInvalid(_trans, "Nudge Forward", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.postSpawnNudgeDwn.LogIfInvalid(_trans, "Nudge Down", wave.SpawnLevelNumber, wave.SpawnWaveNumber);
            wave.postSpawnNudgeRgt.LogIfInvalid(_trans, "Nudge Right", wave.SpawnLevelNumber, wave.SpawnWaveNumber);

            if (wave.curWaveRepeatMode != WaveSpecifics.RepeatWaveMode.UntilWorldVariableAbove &&
                wave.curWaveRepeatMode != WaveSpecifics.RepeatWaveMode.UntilWorldVariableBelow) {
                return;
            }

            // ReSharper disable once ForCanBeConvertedToForeach
            for (var i = 0; i < wave.repeatPassCriteria.statMods.Count; i++) {
                var crit = wave.repeatPassCriteria.statMods[i];

                if (WorldVariableTracker.IsBlankVariableName(crit._statName)) {
                    LevelSettings.LogIfNew(
                        string.Format(
                            "Spawner '{0}', Level {1} Wave {2} has a Repeat Item Limit with no World Variable selected. Please select one.",
                            _trans.name,
                            wave.SpawnLevelNumber + 1,
                            wave.SpawnWaveNumber + 1));
                    _spawnerValid = false;
                } else if (!WorldVariableTracker.VariableExistsInScene(crit._statName)) {
                    LevelSettings.LogIfNew(
                        string.Format(
                            "Spawner '{0}', Level {1} Wave {2} has a Repeat Item Limit using World Variable '{3}', which doesn't exist in the scene.",
                            _trans.name,
                            wave.SpawnLevelNumber + 1,
                            wave.SpawnWaveNumber + 1,
                            crit._statName));
                    _spawnerValid = false;
                } else {
                    switch (crit._varTypeToUse) {
                        case WorldVariableTracker.VariableType._integer:
                            if (crit._modValueIntAmt.variableSource == LevelSettings.VariableSource.Variable) {
                                if (!WorldVariableTracker.VariableExistsInScene(crit._modValueIntAmt.worldVariableName)) {
                                    if (
                                        LevelSettings.IllegalVariableNames.Contains(
                                            crit._modValueIntAmt.worldVariableName)) {
                                        LevelSettings.LogIfNew(
                                            string.Format(
                                                "Spawner '{0}', Level {1} Wave {2} has a Repeat Item Limit criteria with no World Variable selected. Please select one.",
                                                _trans.name,
                                                wave.SpawnLevelNumber + 1,
                                                wave.SpawnWaveNumber + 1));
                                    } else {
                                        LevelSettings.LogIfNew(
                                            string.Format(
                                                "Spawner '{0}', Level {1} Wave {2} has a Repeat Item Limit using the value of World Variable '{3}', which doesn't exist in the Scene.",
                                                _trans.name,
                                                wave.SpawnLevelNumber + 1,
                                                wave.SpawnWaveNumber + 1,
                                                crit._modValueIntAmt.worldVariableName));
                                    }
                                    _spawnerValid = false;
                                }
                            }

                            break;
                        case WorldVariableTracker.VariableType._float:
                            if (crit._modValueIntAmt.variableSource == LevelSettings.VariableSource.Variable) {
                                if (
                                    !WorldVariableTracker.VariableExistsInScene(crit._modValueFloatAmt.worldVariableName)) {
                                    if (
                                        LevelSettings.IllegalVariableNames.Contains(
                                            crit._modValueFloatAmt.worldVariableName)) {
                                        LevelSettings.LogIfNew(
                                            string.Format(
                                                "Spawner '{0}', Level {1} Wave {2} has a Repeat Item Limit criteria with no World Variable selected. Please select one.",
                                                _trans.name,
                                                wave.SpawnLevelNumber + 1,
                                                wave.SpawnWaveNumber + 1));
                                    } else {
                                        LevelSettings.LogIfNew(
                                            string.Format(
                                                "Spawner '{0}', Level {1} Wave {2} has a Repeat Item Limit using the value of World Variable '{3}', which doesn't exist in the Scene.",
                                                _trans.name,
                                                wave.SpawnLevelNumber + 1,
                                                wave.SpawnWaveNumber + 1,
                                                crit._modValueFloatAmt.worldVariableName));
                                    }
                                    _spawnerValid = false;
                                }
                            }

                            break;
                        default:
                            LevelSettings.LogIfNew("Add code for varType: " + crit._varTypeToUse.ToString());
                            break;
                    }
                }
            }
        }
 /// <summary>
 /// This method gets called when a wave is about to repeat.
 /// </summary>
 /// <param name="spec">The wave specifics.</param>
 public virtual void WaveRepeat(WaveSpecifics spec)
 {
     // Please do not manipulate values in the "spec". It is for your read-only information
 }
        private static Quaternion GetSpawnRotationForVisualization(WaveSpecifics wave, Transform spawner,
            int itemSpawnedIndex)
        {
            var euler = Vector3.zero;

            switch (wave.curRotationMode) {
                case WaveSpecifics.RotationMode.UsePrefabRotation:
                    break;
                case WaveSpecifics.RotationMode.UseSpawnerRotation:
                    euler = spawner.transform.rotation.eulerAngles;
                    break;
                case WaveSpecifics.RotationMode.CustomRotation:
                    euler = wave.customRotation;
                    break;
            }

            if (wave.enableRandomizations && wave.randomXRotation) {
                euler.x = Random.Range(wave.randomXRotMin.Value, wave.randomXRotMax.Value);
            } else if (wave.enableIncrements && itemSpawnedIndex > 0) {
                if (wave.enableKeepCenter) {
                    euler.x += (itemSpawnedIndex * wave.incrementRotX.Value -
                                (wave.MinToSpwn.Value * wave.incrementRotX.Value * .5f));
                } else {
                    euler.x += (itemSpawnedIndex * wave.incrementRotX.Value);
                }
            }

            if (wave.enableRandomizations && wave.randomYRotation) {
                euler.y = Random.Range(wave.randomYRotMin.Value, wave.randomYRotMax.Value);
            } else if (wave.enableIncrements && itemSpawnedIndex > 0) {
                if (wave.enableKeepCenter) {
                    euler.y += (itemSpawnedIndex * wave.incrementRotY.Value -
                                (wave.MinToSpwn.Value * wave.incrementRotY.Value * .5f));
                } else {
                    euler.y += (itemSpawnedIndex * wave.incrementRotY.Value);
                }
            }

            if (wave.enableRandomizations && wave.randomZRotation) {
                euler.z = Random.Range(wave.randomZRotMin.Value, wave.randomZRotMax.Value);
            } else if (wave.enableIncrements && itemSpawnedIndex > 0) {
                if (wave.enableKeepCenter) {
                    euler.z += (itemSpawnedIndex * wave.incrementRotZ.Value -
                                (wave.MinToSpwn.Value * wave.incrementRotZ.Value * .5f));
                } else {
                    euler.z += (itemSpawnedIndex * wave.incrementRotZ.Value);
                }
            }

            return Quaternion.Euler(euler);
        }
        public void SpawnWaveVisual(WaveSpecifics wave)
        {
            if (Application.isPlaying) {
                // let's not lock up the CPU!
                return;
            }

            var isSphere = wave.spawnSource != WaveSpecifics.SpawnOrigin.Specific || wave.prefabToSpawn == null;

            for (var i = 0; i < wave.MinToSpwn.Value; i++) {
                var spawnPosition = GetSpawnPositionForVisualization(wave, transform.position, i);

                var rotation = GetSpawnRotationForVisualization(wave, transform, i);

                Transform spawned;

                if (isSphere) {
                    spawned = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
                    spawned.transform.position = spawnPosition;
                    spawned.transform.rotation = rotation;
                } else {
                    // ReSharper disable once PossibleNullReferenceException
                    spawned = ((Transform)Instantiate(wave.prefabToSpawn, spawnPosition, rotation));
                }

                spawned.parent = transform;

                AfterSpawnForVisualization(wave, spawned.transform);
            }
        }
        /// <summary>
        /// This returns the item to spawn. Override this to apply custom logic if needed.
        /// </summary>
        /// <param name="wave">The wave specifics.</param>
        /// <returns>The Transform to spawn.</returns>
        protected virtual Transform GetSpawnable(WaveSpecifics wave)
        {
            if (wave == null) {
                return null;
            }

            switch (wave.spawnSource) {
                case WaveSpecifics.SpawnOrigin.Specific:
                    return wave.prefabToSpawn;
                case WaveSpecifics.SpawnOrigin.PrefabPool:
                    return _wavePool.GetRandomWeightedTransform();
            }

            return null;
        }
        private static Vector3 GetSpawnPositionForVisualization(WaveSpecifics wave, Vector3 pos, int itemSpawnedIndex)
        {
            switch (wave.positionXmode) {
                case WaveSpecifics.PositionMode.CustomPosition:
                    pos.x = wave.customPosX.Value;
                    break;
                case WaveSpecifics.PositionMode.OtherObjectPosition:
                    if (wave.otherObjectX != null) {
                        pos.x = wave.otherObjectX.position.x;
                    }
                    break;
            }

            switch (wave.positionYmode) {
                case WaveSpecifics.PositionMode.CustomPosition:
                    pos.y = wave.customPosY.Value;
                    break;
                case WaveSpecifics.PositionMode.OtherObjectPosition:
                    if (wave.otherObjectY != null) {
                        pos.y = wave.otherObjectY.position.y;
                    }
                    break;
            }

            switch (wave.positionZmode) {
                case WaveSpecifics.PositionMode.CustomPosition:
                    pos.z = wave.customPosZ.Value;
                    break;
                case WaveSpecifics.PositionMode.OtherObjectPosition:
                    if (wave.otherObjectZ != null) {
                        pos.z = wave.otherObjectZ.position.z;
                    }
                    break;
            }

            var addVector = Vector3.zero;

            addVector += wave.WaveOffset;

            if (wave.enableRandomizations) {
                addVector.x += Random.Range(-wave.randomDistX.Value, wave.randomDistX.Value);
                addVector.y += Random.Range(-wave.randomDistY.Value, wave.randomDistY.Value);
                addVector.z += Random.Range(-wave.randomDistZ.Value, wave.randomDistZ.Value);
            }

            if (!wave.enableIncrements || itemSpawnedIndex <= 0) {
                return pos + addVector;
            }
            addVector.x += (wave.incrementPositionX.Value * itemSpawnedIndex);
            addVector.y += (wave.incrementPositionY.Value * itemSpawnedIndex);
            addVector.z += (wave.incrementPositionZ.Value * itemSpawnedIndex);

            return pos + addVector;
        }
        private bool SetupNextWave(bool scanForWave, bool isRestart)
        {
            _repeatTimer = null;

            if (activeMode == LevelSettings.ActiveItemMode.Never) {
                // even in repeating waves.
                return false;
            }

            if (isRestart && _currentWave == null) {
                return false; // can't restart because the current wave isn't configured in this Spawner.
            }

            var shouldInit = scanForWave || isRestart;

            if (scanForWave && !isRestart) {
                // find wave
                _settingUpWave = true;
                _currentWave = FindWave(LevelSettings.CurrentLevel, LevelSettings.CurrentWaveInfo.sequencedWaveNumber);

                // validate for all things that could go wrong!
                if (_currentWave == null || !_currentWave.enableWave) {
                    return false;
                }

                // check "active mode" for conditions
                switch (activeMode) {
                    case LevelSettings.ActiveItemMode.Never:
                        return false;
                    case LevelSettings.ActiveItemMode.IfWorldVariableInRange:
                        if (activeItemCriteria.statMods.Count == 0) {
                            return false;
                        }
                        // ReSharper disable once ForCanBeConvertedToForeach
                        for (var i = 0; i < activeItemCriteria.statMods.Count; i++) {
                            var stat = activeItemCriteria.statMods[i];
                            var variable = WorldVariableTracker.GetWorldVariable(stat._statName);
                            if (variable == null) {
                                return false;
                            }
                            var varVal = stat._varTypeToUse == WorldVariableTracker.VariableType._integer
                                ? variable.CurrentIntValue
                                : variable.CurrentFloatValue;

                            var min = stat._varTypeToUse == WorldVariableTracker.VariableType._integer
                                ? stat._modValueIntMin
                                : stat._modValueFloatMin;
                            var max = stat._varTypeToUse == WorldVariableTracker.VariableType._integer
                                ? stat._modValueIntMax
                                : stat._modValueFloatMax;

                            if (min > max) {
                                LevelSettings.LogIfNew(
                                    "The Min cannot be greater than the Max for Active Item Limit in Syncro Spawner '" +
                                    _trans.name + "'.");
                                return false;
                            }

                            if (varVal < min || varVal > max) {
                                return false;
                            }
                        }

                        break;
                    case LevelSettings.ActiveItemMode.IfWorldVariableOutsideRange:
                        if (activeItemCriteria.statMods.Count == 0) {
                            return false;
                        }
                        // ReSharper disable once ForCanBeConvertedToForeach
                        for (var i = 0; i < activeItemCriteria.statMods.Count; i++) {
                            var stat = activeItemCriteria.statMods[i];
                            var variable = WorldVariableTracker.GetWorldVariable(stat._statName);
                            if (variable == null) {
                                return false;
                            }
                            var varVal = stat._varTypeToUse == WorldVariableTracker.VariableType._integer
                                ? variable.CurrentIntValue
                                : variable.CurrentFloatValue;

                            var min = stat._varTypeToUse == WorldVariableTracker.VariableType._integer
                                ? stat._modValueIntMin
                                : stat._modValueFloatMin;
                            var max = stat._varTypeToUse == WorldVariableTracker.VariableType._integer
                                ? stat._modValueIntMax
                                : stat._modValueFloatMax;

                            if (min > max) {
                                LevelSettings.LogIfNew(
                                    "The Min cannot be greater than the Max for Active Item Limit in Syncro Spawner '" +
                                    _trans.name + "'.");
                                return false;
                            }

                            if (varVal >= min && varVal <= max) {
                                return false;
                            }
                        }

                        break;
                }

                if (_currentWave.MinToSpwn.Value == 0 || _currentWave.MaxToSpwn.Value == 0) {
                    return false;
                }

                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                if (scanForWave &&
                    _currentWave.WaveDelaySec.Value + _currentWave.TimeToSpawnEntireWave.Value >=
                    LevelSettings.CurrentWaveInfo.WaveDuration &&
                    LevelSettings.CurrentWaveInfo.waveType == LevelSettings.WaveType.Timed) {
                    LevelSettings.LogIfNew(
                        string.Format(
                            "Wave TimeToSpawnWholeWave plus Wave DelaySeconds must be less than the current LevelSettings wave duration, occured in spawner: {0}, wave# {1}, level {2}.",
                            name,
                            _currentWave.SpawnWaveNumber + 1,
                            _currentWave.SpawnLevelNumber + 1));
                    return false;
                }

                if (_currentWave.MinToSpwn.Value > _currentWave.MaxToSpwn.Value) {
                    LevelSettings.LogIfNew(
                        string.Format(
                            "Wave MinToSpawn cannot be greater than Wave MaxToSpawn, occured in spawner: {0}, wave# {1}, level {2}.",
                            name,
                            _currentWave.SpawnWaveNumber + 1,
                            _currentWave.SpawnLevelNumber + 1));
                    return false;
                }

                if (_currentWave.repeatWaveUntilNew &&
                    _currentWave.repeatPauseMinimum.Value > _currentWave.repeatPauseMaximum.Value) {
                    LevelSettings.LogIfNew(
                        string.Format(
                            "Wave Repeat Pause Min cannot be greater than Wave Repeat Pause Max, occurred in spawner: {0}, wave# {1}, level {2}.",
                            name,
                            _currentWave.SpawnWaveNumber + 1,
                            _currentWave.SpawnLevelNumber + 1));
                    return false;
                }
            }

            if (LevelSettings.IsLoggingOn) {
                var waveStatus = isRestart ? "Restarting" : string.Empty;
                if (string.IsNullOrEmpty(waveStatus)) {
                    waveStatus = scanForWave ? "Starting" : "Repeating";
                }

                Debug.Log(string.Format("{0} matching wave from spawner: {1}, wave# {2}, level {3}.",
                    waveStatus,
                    name,
                    _currentWave.SpawnWaveNumber + 1,
                    _currentWave.SpawnLevelNumber + 1));
            }

            if (_currentWave.spawnSource == WaveSpecifics.SpawnOrigin.PrefabPool) {
                var poolTrans = LevelSettings.GetFirstMatchingPrefabPool(_currentWave.prefabPoolName);
                if (poolTrans == null) {
                    LevelSettings.LogIfNew(
                        string.Format(
                            "Spawner '{0}' wave# {1}, level {2} is trying to use a Prefab Pool that can't be found.",
                            name,
                            _currentWave.SpawnWaveNumber + 1,
                            _currentWave.SpawnLevelNumber + 1));
                    _spawnerValid = false;
                    _currentWave = null;
                    return false;
                }

                _wavePool = poolTrans;
            } else {
                _wavePool = null;
            }

            _settingUpWave = false;

            CheckForValidVariablesForWave(_currentWave);

            _spawnedWaveMembers.Clear();

            _currentWaveSize = Random.Range(_currentWave.MinToSpwn.Value, _currentWave.MaxToSpwn.Value);
            _currentWaveLength = _currentWave.TimeToSpawnEntireWave.Value;

            _itemsToCompleteWave = (int)(_currentWaveSize * _currentWave.waveCompletePercentage * .01f);

            if (_currentWave.repeatWaveUntilNew) {
                if (shouldInit &&
                    (LevelSettings.CurrentWaveInfo.waveType != LevelSettings.WaveType.Elimination ||
                     _currentWave.curWaveRepeatMode == WaveSpecifics.RepeatWaveMode.Endless)) {
                    // only the first time!
                    _currentWave.repetitionsToDo.Value = int.MaxValue;
                }

                _currentWaveSize += (_waveRepetitionNumber * _currentWave.repeatItemInc.Value);
                _currentWaveSize = Math.Min(_currentWaveSize, _currentWave.repeatItemLmt.Value); // cannot exceed limits

                _currentWaveLength += (_waveRepetitionNumber * _currentWave.repeatTimeInc.Value);
                _currentWaveLength = Math.Min(_currentWaveLength, _currentWave.repeatTimeLmt.Value);
                // cannot exceed limits
            }

            _currentWaveLength = Math.Max(0f, _currentWaveLength);

            if (shouldInit) {
                // not on wave repeat!
                _waveRepetitionNumber = 0;
            }

            _waveStartTime = Time.time;
            _waveFinishedSpawning = false;
            _levelSettingsNotifiedOfCompletion = false;
            _countSpawned = 0;
            _singleSpawnTime = _currentWaveLength / _currentWaveSize;

            if (_currentWave.enableLimits) {
                _currentRandomLimitDistance = Random.Range(-_currentWave.doNotSpawnRandomDist.Value,
                    _currentWave.doNotSpawnRandomDist.Value);
            }

            return true;
        }
Esempio n. 12
0
        public WaveSpecifics Clone()
        {
            var clone = new WaveSpecifics {
                isExpanded = isExpanded,
                enableWave = enableWave,
                visualizeWave = visualizeWave,
                SpawnLevelNumber = SpawnLevelNumber,
                SpawnWaveNumber = SpawnWaveNumber,
                MinToSpwn = MinToSpwn,
                MaxToSpwn = MaxToSpwn,
                WaveDelaySec = WaveDelaySec,
                TimeToSpawnEntireWave = TimeToSpawnEntireWave,
                prefabToSpawn = prefabToSpawn,
                spawnSource = spawnSource,
                prefabPoolIndex = prefabPoolIndex,
                prefabPoolName = prefabPoolName,
                repeatWaveUntilNew = repeatWaveUntilNew,
                waveCompletePercentage = waveCompletePercentage,

                curWaveRepeatMode = curWaveRepeatMode,
                curTimedRepeatWaveMode = curTimedRepeatWaveMode,
                repeatPauseMinimum = repeatPauseMinimum,
                repeatPauseMaximum = repeatPauseMaximum,
                repeatItemInc = repeatItemInc,
                repeatTimeInc = repeatTimeInc,
                repeatItemLmt = repeatItemLmt,
                repeatTimeLmt = repeatTimeLmt,
                repetitionsToDo = repetitionsToDo,
                repeatPassCriteria = repeatPassCriteria,

                waveRepeatBonusesEnabled = waveRepeatBonusesEnabled,
                waveRepeatVariableModifiers = waveRepeatVariableModifiers,
                waveRepeatFireEvents = waveRepeatFireEvents,
                waveRepeatCustomEvents = waveRepeatCustomEvents,

                positionExpanded = positionExpanded,
                positionXmode = positionXmode,
                positionYmode = positionYmode,
                positionZmode = positionZmode,
                customPosX = customPosX,
                customPosY = customPosY,
                customPosZ = customPosZ,

                curRotationMode = curRotationMode,
                customRotation = customRotation,

                enableLimits = enableLimits,
                doNotSpawnIfMbrCloserThan = doNotSpawnIfMbrCloserThan,
                doNotSpawnRandomDist = doNotSpawnRandomDist,

                enableRandomizations = enableRandomizations,
                randomXRotation = randomXRotation,
                randomYRotation = randomYRotation,
                randomZRotation = randomZRotation,
                randomDistX = randomDistX,
                randomDistY = randomDistY,
                randomDistZ = randomDistZ,

                randomXRotMin = randomXRotMin,
                randomXRotMax = randomXRotMax,
                randomYRotMin = randomYRotMin,
                randomYRotMax = randomYRotMax,
                randomZRotMin = randomZRotMin,
                randomZRotMax = randomZRotMax,

                enableIncrements = enableIncrements,
                enableKeepCenter = enableKeepCenter,
                incrementPositionX = incrementPositionX,
                incrementPositionY = incrementPositionY,
                incrementPositionZ = incrementPositionZ,
                incrementRotX = incrementRotX,
                incrementRotY = incrementRotY,
                incrementRotZ = incrementRotZ,

                waveOffsetList = waveOffsetList,

                enablePostSpawnNudge = enablePostSpawnNudge,
                postSpawnNudgeFwd = postSpawnNudgeFwd,
                postSpawnNudgeRgt = postSpawnNudgeRgt,
                postSpawnNudgeDwn = postSpawnNudgeDwn
            };

            return clone;
        }
 /// <summary>
 /// This method gets called when the wave is about to spawn the first item.
 /// </summary>
 /// <param name="spec">The wave specifics.</param>
 public virtual void WaveStart(WaveSpecifics spec)
 {
     // Please do not manipulate values in the "spec". It is for your read-only information
 }
 /// <summary>
 /// This method gets called when an elimination wave has the last item despawned, on the first and every repeat.
 /// </summary>
 /// <param name="spec">The wave specifics.</param>
 public virtual void EliminationWaveCompleted(WaveSpecifics spec)
 {
     // called at the end of each wave, whether or not it is repeating. This is called before the Repeat delay
     // Please do not manipulate values in the "spec". It is for your read-only information
 }
    private void AddBonusStatModifier(string modifierName, WaveSpecifics waveSpec) {
        if (waveSpec.waveRepeatVariableModifiers.HasKey(modifierName)) {
            DTInspectorUtility.ShowAlert("This Wave already has a modifier for World Variable: " + modifierName + ". Please modify that instead.");
            return;
        }

        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "add Wave Repeat Bonus modifier");

        var vType = WorldVariableTracker.GetWorldVariableScript(modifierName);

        waveSpec.waveRepeatVariableModifiers.statMods.Add(new WorldVariableModifier(modifierName, vType.varType));
    }
        private static Vector3 GetSpawnPositionForVisualization(WaveSpecifics wave, Vector3 pos, int itemSpawnedIndex)
        {
            if (wave.positionXmode == WaveSpecifics.PositionMode.CustomPosition) {
                pos.x = wave.customPosX.Value;
            }

            if (wave.positionYmode == WaveSpecifics.PositionMode.CustomPosition) {
                pos.y = wave.customPosY.Value;
            }

            if (wave.positionZmode == WaveSpecifics.PositionMode.CustomPosition) {
                pos.z = wave.customPosZ.Value;
            }

            var addVector = Vector3.zero;

            addVector += wave.WaveOffset;

            if (wave.enableRandomizations) {
                addVector.x += Random.Range(-wave.randomDistX.Value, wave.randomDistX.Value);
                addVector.y += Random.Range(-wave.randomDistY.Value, wave.randomDistY.Value);
                addVector.z += Random.Range(-wave.randomDistZ.Value, wave.randomDistZ.Value);
            }

            if (!wave.enableIncrements || itemSpawnedIndex <= 0) {
                return pos + addVector;
            }
            addVector.x += (wave.incrementPositionX.Value * itemSpawnedIndex);
            addVector.y += (wave.incrementPositionY.Value * itemSpawnedIndex);
            addVector.z += (wave.incrementPositionZ.Value * itemSpawnedIndex);

            return pos + addVector;
        }
    // ReSharper disable once FunctionComplexityOverflow
    public override void OnInspectorGUI() {
        EditorGUIUtility.LookLikeControls();

        _settings = (WaveSyncroPrefabSpawner)target;

        WorldVariableTracker.ClearInGamePlayerStats();

        _isDirty = false;

        var myParent = _settings.transform.parent;
        Transform levelSettingObj = null;
        LevelSettings levelSettings = null;

        LevelSettings.Instance = null; // clear cached version

        if (myParent != null) {
            levelSettingObj = myParent.parent;
            if (levelSettingObj != null) {
                levelSettings = levelSettingObj.GetComponent<LevelSettings>();
            }
        }

        if (myParent == null || levelSettingObj == null || levelSettings == null) {
            DrawDefaultInspector();
            return;
        }

        var allStats = KillerVariablesHelper.AllStatNames;

        DTInspectorUtility.DrawTexture(CoreGameKitInspectorResources.LogoTexture);

        EditorGUI.indentLevel = 0;

        DTInspectorUtility.StartGroupHeader();

        var waveActivated = false;

        var newActive = (LevelSettings.ActiveItemMode)EditorGUILayout.EnumPopup("Active Mode", _settings.activeMode);
        if (newActive != _settings.activeMode) {
            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Active Mode");
            _settings.activeMode = newActive;
            if (!Application.isPlaying) {
                _settings.gameObject.DestroyChildrenImmediate();
            }

            if (newActive != LevelSettings.ActiveItemMode.Never) {
                waveActivated = true;
            }
        }
        EditorGUILayout.EndVertical();

        switch (_settings.activeMode) {
            case LevelSettings.ActiveItemMode.IfWorldVariableInRange:
            case LevelSettings.ActiveItemMode.IfWorldVariableOutsideRange:
                var missingStatNames = new List<string>();
                missingStatNames.AddRange(allStats);
                missingStatNames.RemoveAll(delegate(string obj) {
                    return _settings.activeItemCriteria.HasKey(obj);
                });

                var newStat = EditorGUILayout.Popup("Add Active Limit", 0, missingStatNames.ToArray());
                if (newStat != 0) {
                    AddActiveLimit(missingStatNames[newStat]);
                }

                if (_settings.activeItemCriteria.statMods.Count == 0) {
                    DTInspectorUtility.ShowRedErrorBox("You have no Active Limits. Spawner will never be Active.");
                } else {
                    EditorGUILayout.Separator();

                    int? indexToDelete = null;

                    for (var j = 0; j < _settings.activeItemCriteria.statMods.Count; j++) {
                        var modifier = _settings.activeItemCriteria.statMods[j];
                        EditorGUILayout.BeginHorizontal();
                        GUILayout.Space(15);
                        var statName = modifier._statName;
                        GUILayout.Label(statName);

                        GUILayout.FlexibleSpace();
                        GUILayout.Label("Min");
                        switch (modifier._varTypeToUse) {
                            case WorldVariableTracker.VariableType._integer:
                                var newMin = EditorGUILayout.IntField(modifier._modValueIntMin, GUILayout.MaxWidth(60));
                                if (newMin != modifier._modValueIntMin) {
                                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Limit Min");
                                    modifier._modValueIntMin = newMin;
                                }
                                GUILayout.Label("Max");

                                var newMax = EditorGUILayout.IntField(modifier._modValueIntMax, GUILayout.MaxWidth(60));
                                if (newMax != modifier._modValueIntMax) {
                                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Limit Max");
                                    modifier._modValueIntMax = newMax;
                                }
                                break;
                            case WorldVariableTracker.VariableType._float:
                                var newMinFloat = EditorGUILayout.FloatField(modifier._modValueFloatMin, GUILayout.MaxWidth(60));
                                if (newMinFloat != modifier._modValueFloatMin) {
                                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Limit Min");
                                    modifier._modValueFloatMin = newMinFloat;
                                }
                                GUILayout.Label("Max");

                                var newMaxFloat = EditorGUILayout.FloatField(modifier._modValueFloatMax, GUILayout.MaxWidth(60));
                                if (newMaxFloat != modifier._modValueFloatMax) {
                                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Limit Max");
                                    modifier._modValueFloatMax = newMaxFloat;
                                }
                                break;
                            default:
                                Debug.LogError("Add code for varType: " + modifier._varTypeToUse.ToString());
                                break;
                        }
                        GUI.backgroundColor = DTInspectorUtility.DeleteButtonColor;
                        if (GUILayout.Button(new GUIContent("Delete", "Remove this Limit"), EditorStyles.miniButton, GUILayout.MaxWidth(45))) {
                            indexToDelete = j;
                        }

                        GUI.backgroundColor = Color.white;
                        GUILayout.Space(5);
                        EditorGUILayout.EndHorizontal();

                        KillerVariablesHelper.ShowErrorIfMissingVariable(modifier._statName);

                        var min = modifier._varTypeToUse == WorldVariableTracker.VariableType._integer ? modifier._modValueIntMin : modifier._modValueFloatMin;
                        var max = modifier._varTypeToUse == WorldVariableTracker.VariableType._integer ? modifier._modValueIntMax : modifier._modValueFloatMax;

                        if (min > max) {
                            DTInspectorUtility.ShowRedErrorBox(modifier._statName + " Min cannot exceed Max, please fix!");
                        }
                    }

                    DTInspectorUtility.ShowColorWarningBox("  Limits are inclusive: i.e. 'Above' means >=");
                    if (indexToDelete.HasValue) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "delete Limit");
                        _settings.activeItemCriteria.DeleteByIndex(indexToDelete.Value);
                    }
                }

                break;
        }
        EditorGUILayout.EndVertical();

        var newGO = (TriggeredSpawner.GameOverBehavior)EditorGUILayout.EnumPopup("Game Over Behavior", _settings.gameOverBehavior);
        if (newGO != _settings.gameOverBehavior) {
            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Game Over Behavior");
            _settings.gameOverBehavior = newGO;
        }

        var newPause = (TriggeredSpawner.WavePauseBehavior)EditorGUILayout.EnumPopup("Wave Pause Behavior", _settings.wavePauseBehavior);
        if (newPause != _settings.wavePauseBehavior) {
            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Wave Pause Behavior");
            _settings.wavePauseBehavior = newPause;
        }

        EditorGUI.indentLevel = 0;
        var hadNoListener = _settings.listener == null;
        var newListener = (WaveSyncroSpawnerListener)EditorGUILayout.ObjectField("Listener", _settings.listener, typeof(WaveSyncroSpawnerListener), true);
        if (newListener != _settings.listener) {
            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "assign Listener");
            _settings.listener = newListener;
            if (hadNoListener && _settings.listener != null) {
                _settings.listener.sourceSpawnerName = _settings.transform.name;
            }
        }

        DTInspectorUtility.StartGroupHeader();
        var newUseLayer = (WaveSyncroPrefabSpawner.SpawnLayerTagMode)EditorGUILayout.EnumPopup("Spawn Layer Mode", _settings.spawnLayerMode);
        if (newUseLayer != _settings.spawnLayerMode) {
            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Spawn Layer Mode");
            _settings.spawnLayerMode = newUseLayer;
        }
        EditorGUILayout.EndVertical();

        if (_settings.spawnLayerMode == WaveSyncroPrefabSpawner.SpawnLayerTagMode.Custom) {
            EditorGUI.indentLevel = 0;

            var newCustomLayer = EditorGUILayout.LayerField("Custom Spawn Layer", _settings.spawnCustomLayer);
            if (newCustomLayer != _settings.spawnCustomLayer) {
                UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Custom Spawn Layer");
                _settings.spawnCustomLayer = newCustomLayer;
            }
        }
        EditorGUILayout.EndVertical();

        DTInspectorUtility.AddSpaceForNonU5();

        DTInspectorUtility.StartGroupHeader();
        EditorGUI.indentLevel = 0;
        var newUseTag = (WaveSyncroPrefabSpawner.SpawnLayerTagMode)EditorGUILayout.EnumPopup("Spawn Tag Mode", _settings.spawnTagMode);
        if (newUseTag != _settings.spawnTagMode) {
            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Spawn Tag Mode");
            _settings.spawnTagMode = newUseTag;
        }
        EditorGUILayout.EndVertical();

        if (_settings.spawnTagMode == WaveSyncroPrefabSpawner.SpawnLayerTagMode.Custom) {
            EditorGUI.indentLevel = 0;
            var newCustomTag = EditorGUILayout.TagField("Custom Spawn Tag", _settings.spawnCustomTag);
            if (newCustomTag != _settings.spawnCustomTag) {
                UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Custom Spawn Tag");
                _settings.spawnCustomTag = newCustomTag;
            }
        }
        EditorGUILayout.EndVertical();

        EditorGUILayout.Separator();
        EditorGUI.indentLevel = 0;

        var disabledText = "";
        if (_settings.activeMode == LevelSettings.ActiveItemMode.Never) {
            disabledText = " --DISABLED--";
        }

        var newExpanded = _settings.isExpanded;
        var text = string.Format("Wave Settings ({0}){1}", _settings.waveSpecs.Count, disabledText);

        // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
        if (!newExpanded) {
            GUI.backgroundColor = DTInspectorUtility.InactiveHeaderColor;
        } else {
            GUI.backgroundColor = DTInspectorUtility.ActiveHeaderColor;
        }

        GUILayout.BeginHorizontal();

#if UNITY_3_5_7
        if (!newExpanded) {
            text += " (Click to expand)";
        }
#else
        text = "<b><size=11>" + text + "</size></b>";
#endif
        if (newExpanded) {
            text = "\u25BC " + text;
        } else {
            text = "\u25BA " + text;
        }
        if (!GUILayout.Toggle(true, text, "dragtab", GUILayout.MinWidth(20f))) {
            newExpanded = !newExpanded;
        }

        GUILayout.Space(2f);

        if (newExpanded != _settings.isExpanded) {
            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle expand Wave Settings");
            _settings.isExpanded = newExpanded;
        }
        // BUTTONS...
        EditorGUILayout.BeginHorizontal(GUILayout.MinWidth(16));

        // ReSharper disable once RedundantAssignment

        var showVisualization = false;

        if (_settings.activeMode != LevelSettings.ActiveItemMode.Never) {
            GUI.contentColor = DTInspectorUtility.BrightButtonColor;
            GUI.backgroundColor = Color.white;

            // Add expand/collapse buttons if there are items in the list
            if (_settings.waveSpecs.Count > 0) {
                var content = new GUIContent("Collapse", "Click to collapse all");
                var masterCollapse = GUILayout.Button(content, EditorStyles.toolbarButton, GUILayout.Height(16));

                content = new GUIContent("Expand", "Click to expand all");
                var masterExpand = GUILayout.Button(content, EditorStyles.toolbarButton, GUILayout.Height(16));
                if (masterExpand) {
                    ExpandCollapseAll(true);
                }
                if (masterCollapse) {
                    ExpandCollapseAll(false);
                }
            } else {
                GUILayout.FlexibleSpace();
            }

            EditorGUILayout.BeginHorizontal(GUILayout.MinWidth(50));
            // A little space between button groups

            var addText = string.Format("Click to add Wave{0}.", _settings.waveSpecs.Count > 0 ? " before the first" : "");
            GUI.contentColor = DTInspectorUtility.BrightButtonColor;

            GUI.contentColor = DTInspectorUtility.AddButtonColor;
            // Main Add button
            if (GUILayout.Button(new GUIContent("Add", addText), EditorStyles.toolbarButton, GUILayout.Height(16))) {
                if (levelSettings.LevelTimes.Count == 0) {
                    DTInspectorUtility.ShowAlert("You will not have any Level or Wave #'s to select in your Spawner Wave Settings until you add a Level in LevelSettings. Please do that first.");
                } else {
                    var newWave = new WaveSpecifics();
                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "add Wave");
                    _settings.waveSpecs.Add(newWave);
                }
            }

            GUILayout.Space(4);
            GUI.contentColor = Color.white;

            EditorGUILayout.EndHorizontal();

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndHorizontal();

            if (_settings.isExpanded) {
                DTInspectorUtility.BeginGroupedControls();
                EditorGUI.indentLevel = 0;

                if (_settings.waveSpecs.Count == 0) {
                    DTInspectorUtility.ShowLargeBarAlertBox("You have zero Wave Settings. Your spawner won't spawn anything.");
                }

                var waveToInsertAt = -1;
                WaveSpecifics waveToDelete = null;
                WaveSpecifics waveSetting;
                int? waveToMoveUp = null;
                int? waveToMoveDown = null;
                int? waveToClone = null;

                // get list of prefab pools.
                var poolNames = LevelSettings.GetSortedPrefabPoolNames();

                for (var w = 0; w < _settings.waveSpecs.Count; w++) {
                    EditorGUI.indentLevel = 1;
                    waveSetting = _settings.waveSpecs[w];
                    var levelWave = GetLevelWaveFromWaveSpec(waveSetting);

                    DTInspectorUtility.StartGroupHeader();
                    EditorGUILayout.BeginHorizontal();

                    var sDisabled = "";
                    if (!waveSetting.isExpanded && !waveSetting.enableWave) {
                        sDisabled = " DISABLED ";
                    }

                    newExpanded = DTInspectorUtility.Foldout(waveSetting.isExpanded,
                      string.Format("Wave Setting #{0} ({1}/{2}){3}", (w + 1),
                              waveSetting.SpawnLevelNumber + 1,
                              waveSetting.SpawnWaveNumber + 1,
                              sDisabled));
                    if (newExpanded != waveSetting.isExpanded) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle expand Wave Setting");
                        waveSetting.isExpanded = newExpanded;
                    }

                    GUILayout.FlexibleSpace();
                    var waveButtonPressed = DTInspectorUtility.AddFoldOutListItemButtons(w, _settings.waveSpecs.Count, "Wave", false, true, true);
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.EndVertical();

                    switch (waveButtonPressed) {
                        case DTInspectorUtility.FunctionButtons.Remove:
                            waveToDelete = waveSetting;
                            _isDirty = true;
                            break;
                        case DTInspectorUtility.FunctionButtons.Add:
                            waveToInsertAt = w;
                            _isDirty = true;
                            break;
                        case DTInspectorUtility.FunctionButtons.ShiftDown:
                            waveToMoveDown = w;
                            _isDirty = true;
                            break;
                        case DTInspectorUtility.FunctionButtons.ShiftUp:
                            waveToMoveUp = w;
                            _isDirty = true;
                            break;
                        case DTInspectorUtility.FunctionButtons.Copy:
                            waveToClone = w;
                            _isDirty = true;
                            break;
                    }

                    if (!waveSetting.isExpanded) {
                        EditorGUILayout.EndVertical();
                        continue;
                    }
                    EditorGUI.indentLevel = 0;

                    var waveEnabled = false;
                    var newEnabled = EditorGUILayout.BeginToggleGroup(" Enable Wave", waveSetting.enableWave);
                    if (newEnabled != waveSetting.enableWave) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Enable Wave");
                        waveSetting.enableWave = newEnabled;
                        if (!Application.isPlaying) {
                            _settings.gameObject.DestroyChildrenImmediate();
                        }

                        if (newEnabled) {
                            waveEnabled = true;
                        }
                    }

                    var newVis = EditorGUILayout.Toggle("Visualize Wave", waveSetting.visualizeWave);
                    if (newVis != waveSetting.visualizeWave) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Visualize Wave");
                        waveSetting.visualizeWave = newVis;
                        if (!newVis) {
                            _settings.gameObject.DestroyChildrenImmediate();
                        } else {
                            showVisualization = true;
                        }
                    }

                    var oldLevelNumber = waveSetting.SpawnLevelNumber;

                    var newLevel = EditorGUILayout.IntPopup("Level#", waveSetting.SpawnLevelNumber + 1, LevelNames, LevelIndexes) - 1;
                    if (newLevel != waveSetting.SpawnLevelNumber) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Level#");
                        waveSetting.SpawnLevelNumber = newLevel;

                        if (oldLevelNumber != waveSetting.SpawnLevelNumber) {
                            waveSetting.SpawnWaveNumber = 0;
                        }
                    }

                    var newWave = EditorGUILayout.IntPopup("Wave#", waveSetting.SpawnWaveNumber + 1,
                        WaveNamesForLevel(waveSetting.SpawnLevelNumber), WaveIndexesForLevel(waveSetting.SpawnLevelNumber)) - 1;
                    if (newWave != waveSetting.SpawnWaveNumber) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Wave#");
                        waveSetting.SpawnWaveNumber = newWave;
                    }

                    var oldInt = waveSetting.MinToSpwn.Value;

                    KillerVariablesHelper.DisplayKillerInt(ref _isDirty, waveSetting.MinToSpwn, "Min To Spawn", _settings);
                    if (oldInt != waveSetting.MinToSpwn.Value) {
                        showVisualization = true;
                    }

                    KillerVariablesHelper.DisplayKillerInt(ref _isDirty, waveSetting.MaxToSpwn, "Max To Spawn", _settings);

                    KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.TimeToSpawnEntireWave, "Time To Spawn All", _settings);

                    KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.WaveDelaySec, "Delay Wave (sec)", _settings);

                    if (levelWave.waveType == LevelSettings.WaveType.Elimination) {
                        var newComplete = EditorGUILayout.IntSlider("Wave Completion %", waveSetting.waveCompletePercentage, 1, 100);
                        if (newComplete != waveSetting.waveCompletePercentage) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "Wave Completion %");
                            waveSetting.waveCompletePercentage = newComplete;
                        }
                    }

                    DTInspectorUtility.StartGroupHeader(1);

                    var newSource = (WaveSpecifics.SpawnOrigin)EditorGUILayout.EnumPopup("Prefab Type", waveSetting.spawnSource);
                    if (newSource != waveSetting.spawnSource) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Prefab Type");
                        waveSetting.spawnSource = newSource;
                        showVisualization = true;
                    }
                    EditorGUILayout.EndVertical();
                    switch (waveSetting.spawnSource) {
                        case WaveSpecifics.SpawnOrigin.Specific:
                            var newPrefab = (Transform)EditorGUILayout.ObjectField("Prefab To Spawn", waveSetting.prefabToSpawn, typeof(Transform), true);
                            if (newPrefab != waveSetting.prefabToSpawn) {
                                UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Prefab To Spawn");
                                waveSetting.prefabToSpawn = newPrefab;
                            }
                            if (waveSetting.prefabToSpawn == null) {
                                DTInspectorUtility.ShowRedErrorBox("Please specify a prefab to spawn.");
                            }
                            break;
                        case WaveSpecifics.SpawnOrigin.PrefabPool:
                            if (poolNames != null) {
                                var pool = LevelSettings.GetFirstMatchingPrefabPool(waveSetting.prefabPoolName);
                                var noPoolSelected = false;
                                var illegalPool = false;
                                var noPools = false;

                                if (pool == null) {
                                    if (string.IsNullOrEmpty(waveSetting.prefabPoolName)) {
                                        noPoolSelected = true;
                                    } else {
                                        illegalPool = true;
                                    }
                                    waveSetting.prefabPoolIndex = 0;
                                } else {
                                    waveSetting.prefabPoolIndex = poolNames.IndexOf(waveSetting.prefabPoolName);
                                }

                                if (poolNames.Count > 1) {
                                    var newPoolIndex = EditorGUILayout.Popup("Prefab Pool", waveSetting.prefabPoolIndex, poolNames.ToArray());
                                    if (newPoolIndex != waveSetting.prefabPoolIndex) {
                                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Prefab Pool");
                                        waveSetting.prefabPoolIndex = newPoolIndex;
                                    }

                                    if (waveSetting.prefabPoolIndex > 0) {
                                        var matchingPool = LevelSettings.GetFirstMatchingPrefabPool(poolNames[waveSetting.prefabPoolIndex]);
                                        if (matchingPool != null) {
                                            waveSetting.prefabPoolName = matchingPool.name;
                                        }
                                    } else {
                                        waveSetting.prefabPoolName = string.Empty;
                                    }
                                } else {
                                    noPools = true;
                                }

                                if (noPools) {
                                    DTInspectorUtility.ShowRedErrorBox("You have no Prefab Pools. Create one first.");
                                } else if (noPoolSelected) {
                                    DTInspectorUtility.ShowRedErrorBox("No Prefab Pool selected.");
                                } else if (illegalPool) {
                                    DTInspectorUtility.ShowRedErrorBox("Prefab Pool '" + waveSetting.prefabPoolName + "' not found. Select one.");
                                }
                            } else {
                                DTInspectorUtility.ShowRedErrorBox(LevelSettings.NoPrefabPoolsContainerAlert);
                                DTInspectorUtility.ShowRedErrorBox(LevelSettings.RevertLevelSettingsAlert);
                            }

                            break;
                    }
                    EditorGUILayout.EndVertical();
                    DTInspectorUtility.AddSpaceForNonU5();

                    DTInspectorUtility.StartGroupHeader(1);

                    EditorGUI.indentLevel = 1;
                    var newEx = DTInspectorUtility.Foldout(waveSetting.positionExpanded, " Position Settings");
                    if (newEx != waveSetting.positionExpanded) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle expand Position Settings");
                        waveSetting.positionExpanded = newEx;
                    }
                    EditorGUILayout.EndVertical();
                    EditorGUI.indentLevel = 0;

                    if (waveSetting.positionExpanded) {
                        var newX = (WaveSpecifics.PositionMode)EditorGUILayout.EnumPopup("X Position Mode", waveSetting.positionXmode);
                        if (newX != waveSetting.positionXmode) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change X Position Mode");
                            waveSetting.positionXmode = newX;
                            showVisualization = true;
                        }

                        if (waveSetting.positionXmode == WaveSpecifics.PositionMode.CustomPosition) {
                            var oldX = waveSetting.customPosX.Value;
                            KillerVariablesHelper.DisplayKillerInt(ref _isDirty, waveSetting.customPosX, "X Position", _settings);
                            if (oldX != waveSetting.customPosX.Value) {
                                showVisualization = true;
                            }
                        }

                        var newY = (WaveSpecifics.PositionMode)EditorGUILayout.EnumPopup("Y Position Mode", waveSetting.positionYmode);
                        if (newY != waveSetting.positionYmode) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Y Position Mode");
                            waveSetting.positionYmode = newY;
                            showVisualization = true;
                        }

                        if (waveSetting.positionYmode == WaveSpecifics.PositionMode.CustomPosition) {
                            var oldY = waveSetting.customPosY.Value;
                            KillerVariablesHelper.DisplayKillerInt(ref _isDirty, waveSetting.customPosY, "Y Position", _settings);
                            if (oldY != waveSetting.customPosY.Value) {
                                showVisualization = true;
                            }
                        }

                        var newZ = (WaveSpecifics.PositionMode)EditorGUILayout.EnumPopup("Z Position Mode", waveSetting.positionZmode);
                        if (newZ != waveSetting.positionZmode) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Z Position Mode");
                            waveSetting.positionZmode = newZ;
                            showVisualization = true;
                        }

                        if (waveSetting.positionZmode == WaveSpecifics.PositionMode.CustomPosition) {
                            var oldZ = waveSetting.customPosZ.Value;
                            KillerVariablesHelper.DisplayKillerInt(ref _isDirty, waveSetting.customPosZ, "Z Position", _settings);
                            if (oldZ != waveSetting.customPosZ.Value) {
                                showVisualization = true;
                            }
                        }

                        if (waveSetting.waveOffsetList.Count == 0) {
                            waveSetting.waveOffsetList.Add(new Vector3());
                            _isDirty = true;
                            showVisualization = true;
                        }

                        DTInspectorUtility.StartGroupHeader(0);
                        EditorGUILayout.BeginHorizontal();
                        EditorGUILayout.LabelField("Wave Offsets (Randomly chosen)");

                        if (!Application.isPlaying) {
                            GUI.contentColor = DTInspectorUtility.AddButtonColor;
                            if (GUILayout.Button(new GUIContent("Add", "Add a new Wave Offset"),
                                EditorStyles.toolbarButton, GUILayout.MaxWidth(50))) {
                                waveSetting.waveOffsetList.Add(new Vector3());
                                _isDirty = true;
                                showVisualization = true;
                            }
                            GUI.contentColor = Color.white;
                        }

                        EditorGUILayout.EndHorizontal();
                        EditorGUILayout.EndVertical();

                        int? itemToDelete = null;

                        // ReSharper disable once ForCanBeConvertedToForeach
                        for (var i = 0; i < waveSetting.waveOffsetList.Count; i++) {
                            var anOffset = waveSetting.waveOffsetList[i];
                            EditorGUILayout.BeginHorizontal();

                            var newOffset = EditorGUILayout.Vector3Field("Wave Offset #" + (i + 1), anOffset);

                            var btn = DTInspectorUtility.FunctionButtons.None;

                            if (!Application.isPlaying) {
                                btn = DTInspectorUtility.AddCustomEventDeleteIcon(false, "Wave Offset");
                            }

                            EditorGUILayout.EndHorizontal();

                            if (btn == DTInspectorUtility.FunctionButtons.Remove) {
                                itemToDelete = i;
                            }

                            if (newOffset == anOffset) {
                                continue;
                            }

                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Wave Offset");
                            waveSetting.waveOffsetList[i] = newOffset;
                            showVisualization = true;
                        }
                        EditorGUILayout.EndVertical();

                        if (itemToDelete.HasValue) {
                            waveSetting.waveOffsetList.RemoveAt(itemToDelete.Value);
                            _isDirty = true;
                            showVisualization = true;
                        }
                    }

                    EditorGUILayout.EndVertical();
                    DTInspectorUtility.AddSpaceForNonU5();

                    DTInspectorUtility.StartGroupHeader(1);
                    var newRotation = (WaveSpecifics.RotationMode)EditorGUILayout.EnumPopup("Spawn Rotation Mode", waveSetting.curRotationMode);
                    if (newRotation != waveSetting.curRotationMode) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Rotation Mode");
                        waveSetting.curRotationMode = newRotation;
                        showVisualization = true;
                    }
                    EditorGUILayout.EndVertical();

                    if (waveSetting.curRotationMode == WaveSpecifics.RotationMode.CustomRotation) {
                        var newCust = EditorGUILayout.Vector3Field("Custom Rotation Euler", waveSetting.customRotation);
                        if (newCust != waveSetting.customRotation) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Custom Rotation Euler");
                            waveSetting.customRotation = newCust;
                            showVisualization = true;
                        }
                    }
                    EditorGUILayout.EndVertical();
                    DTInspectorUtility.AddSpaceForNonU5();

                    DTInspectorUtility.StartGroupHeader(1);
                    newExpanded = EditorGUILayout.BeginToggleGroup(" Spawn Limit Controls", waveSetting.enableLimits);
                    if (newExpanded != waveSetting.enableLimits) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Spawn Limit Controls");
                        waveSetting.enableLimits = newExpanded;
                    }
                    DTInspectorUtility.EndGroupHeader();
                    if (waveSetting.enableLimits) {
                        DTInspectorUtility.ShowColorWarningBox("Stop spawning until all spawns from wave satisfy:");

                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.doNotSpawnIfMbrCloserThan, "Min. Distance", _settings);

                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.doNotSpawnRandomDist, "Random Distance", _settings);
                    }
                    EditorGUILayout.EndToggleGroup();
                    DTInspectorUtility.AddSpaceForNonU5();

                    DTInspectorUtility.StartGroupHeader(1);

                    newExpanded = EditorGUILayout.BeginToggleGroup(" Repeat Wave", waveSetting.repeatWaveUntilNew);
                    if (newExpanded != waveSetting.repeatWaveUntilNew) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Repeat Wave");
                        waveSetting.repeatWaveUntilNew = newExpanded;
                    }
                    DTInspectorUtility.EndGroupHeader();
                    if (waveSetting.repeatWaveUntilNew) {
                        if (levelWave.waveType == LevelSettings.WaveType.Elimination) {
                            var newRepeatMode = (WaveSpecifics.RepeatWaveMode)EditorGUILayout.EnumPopup("Repeat Mode", waveSetting.curWaveRepeatMode);
                            if (newRepeatMode != waveSetting.curWaveRepeatMode) {
                                UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Repeat Mode");
                                waveSetting.curWaveRepeatMode = newRepeatMode;
                            }
                        } else {
                            // only one mode for non-elimination waves.
                            var newRepeatMode = (WaveSpecifics.TimedRepeatWaveMode)EditorGUILayout.EnumPopup("Timed Repeat Mode", waveSetting.curTimedRepeatWaveMode);
                            if (newRepeatMode != waveSetting.curTimedRepeatWaveMode) {
                                UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "change Timed Repeat Mode");
                                waveSetting.curTimedRepeatWaveMode = newRepeatMode;
                            }
                        }

                        switch (waveSetting.curWaveRepeatMode) {
                            case WaveSpecifics.RepeatWaveMode.NumberOfRepetitions:
                                if (levelWave.waveType == LevelSettings.WaveType.Elimination) {
                                    KillerVariablesHelper.DisplayKillerInt(ref _isDirty, waveSetting.repetitionsToDo, "Repetitions", _settings);
                                }
                                break;
                            case WaveSpecifics.RepeatWaveMode.UntilWorldVariableAbove:
                            case WaveSpecifics.RepeatWaveMode.UntilWorldVariableBelow:
                                if (levelWave.waveType != LevelSettings.WaveType.Elimination) {
                                    break;
                                }

                                var missingStatNames = new List<string>();
                                missingStatNames.AddRange(allStats);
                                missingStatNames.RemoveAll(delegate(string obj) {
                                    return waveSetting.repeatPassCriteria.HasKey(obj);
                                });

                                var newStat = EditorGUILayout.Popup("Add Variable Limit", 0, missingStatNames.ToArray());
                                if (newStat != 0) {
                                    AddStatModifier(missingStatNames[newStat], waveSetting);
                                }

                                if (waveSetting.repeatPassCriteria.statMods.Count == 0) {
                                    DTInspectorUtility.ShowRedErrorBox("You have no Variable Limits. Wave will not repeat.");
                                } else {
                                    EditorGUILayout.Separator();

                                    int? indexToDelete = null;

                                    for (var i = 0; i < waveSetting.repeatPassCriteria.statMods.Count; i++) {
                                        var modifier = waveSetting.repeatPassCriteria.statMods[i];
                                        var buttonPressed = KillerVariablesHelper.DisplayKillerInt(ref _isDirty, modifier._modValueIntAmt, modifier._statName, _settings, true, true);
                                        if (buttonPressed == DTInspectorUtility.FunctionButtons.Remove) {
                                            indexToDelete = i;
                                        }
                                    }

                                    DTInspectorUtility.ShowColorWarningBox("Limits are inclusive: i.e. 'Above' means >=");
                                    if (indexToDelete.HasValue) {
                                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "delete Limit");
                                        waveSetting.repeatPassCriteria.DeleteByIndex(indexToDelete.Value);
                                    }

                                    EditorGUILayout.Separator();
                                }
                                break;
                        }

                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.repeatPauseMinimum, "Repeat Pause Min", _settings);

                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.repeatPauseMaximum, "Repeat Pause Max", _settings);

                        KillerVariablesHelper.DisplayKillerInt(ref _isDirty, waveSetting.repeatItemInc, "Spawn Increase", _settings);

                        KillerVariablesHelper.DisplayKillerInt(ref _isDirty, waveSetting.repeatItemLmt, "Spawn Limit", _settings);

                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.repeatTimeInc, "Time Increase", _settings);

                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.repeatTimeLmt, "Time Limit", _settings);


                        // repeat wave variable modifiers
                        DTInspectorUtility.StartGroupHeader(0, false);
                        var newBonusesEnabled = EditorGUILayout.Toggle("Repeat Bonus", waveSetting.waveRepeatBonusesEnabled);
                        if (newBonusesEnabled != waveSetting.waveRepeatBonusesEnabled) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Repeat Bonus");
                            waveSetting.waveRepeatBonusesEnabled = newBonusesEnabled;
                        }

                        if (waveSetting.waveRepeatBonusesEnabled) {
                            EditorGUI.indentLevel = 1;

                            var missingBonusStatNames = new List<string>();
                            missingBonusStatNames.AddRange(allStats);
                            missingBonusStatNames.RemoveAll(delegate(string obj) {
                                return waveSetting.waveRepeatVariableModifiers.HasKey(obj);
                            });

                            var newBonusStat = EditorGUILayout.Popup("Add Variable Modifer", 0, missingBonusStatNames.ToArray());
                            if (newBonusStat != 0) {
                                AddBonusStatModifier(missingBonusStatNames[newBonusStat], waveSetting);
                            }

                            if (waveSetting.waveRepeatVariableModifiers.statMods.Count == 0) {
                                if (waveSetting.waveRepeatBonusesEnabled) {
                                    DTInspectorUtility.ShowColorWarningBox("You currently are using no modifiers for this wave.");
                                }
                            } else {
                                EditorGUILayout.Separator();

                                int? indexToDelete = null;

                                EditorGUI.indentLevel = 0;
                                for (var i = 0; i < waveSetting.waveRepeatVariableModifiers.statMods.Count; i++) {
                                    var modifier = waveSetting.waveRepeatVariableModifiers.statMods[i];

                                    var buttonPressed = DTInspectorUtility.FunctionButtons.None;
                                    switch (modifier._varTypeToUse) {
                                        case WorldVariableTracker.VariableType._integer:
                                            buttonPressed = KillerVariablesHelper.DisplayKillerInt(ref _isDirty, modifier._modValueIntAmt, modifier._statName, _settings, true, true);
                                            break;
                                        case WorldVariableTracker.VariableType._float:
                                            buttonPressed = KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, modifier._modValueFloatAmt, modifier._statName, _settings, true, true);
                                            break;
                                        default:
                                            Debug.LogError("Add code for varType: " + modifier._varTypeToUse.ToString());
                                            break;
                                    }

                                    KillerVariablesHelper.ShowErrorIfMissingVariable(modifier._statName);

                                    if (buttonPressed == DTInspectorUtility.FunctionButtons.Remove) {
                                        indexToDelete = i;
                                    }
                                }

                                if (indexToDelete.HasValue) {
                                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "delete Variable Modifier");
                                    waveSetting.waveRepeatVariableModifiers.DeleteByIndex(indexToDelete.Value);
                                }

                                EditorGUILayout.Separator();
                            }
                        }
                        EditorGUILayout.EndVertical();

                        DTInspectorUtility.AddSpaceForNonU5();

                        DTInspectorUtility.StartGroupHeader(0, false);
                        var newExp = EditorGUILayout.Toggle("Repeat Cust. Events", waveSetting.waveRepeatFireEvents);
                        if (newExp != waveSetting.waveRepeatFireEvents) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Repeat Cust. Events");
                            waveSetting.waveRepeatFireEvents = newExp;
                        }

                        if (waveSetting.waveRepeatFireEvents) {
                            DTInspectorUtility.ShowColorWarningBox("When wave repeats, fire the Custom Events below");

                            EditorGUILayout.BeginHorizontal();
                            GUI.contentColor = DTInspectorUtility.AddButtonColor;
                            GUILayout.Space(10);
                            if (GUILayout.Button(new GUIContent("Add", "Click to add a Custom Event"), EditorStyles.toolbarButton, GUILayout.Width(50))) {
                                UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "Add Wave Repeat Custom Event");
                                waveSetting.waveRepeatCustomEvents.Add(new CGKCustomEventToFire());
                            }
                            GUILayout.Space(10);
                            if (GUILayout.Button(new GUIContent("Remove", "Click to remove the last Custom Event"), EditorStyles.toolbarButton, GUILayout.Width(50))) {
                                UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "Remove last Wave Repeat Custom Event");
                                waveSetting.waveRepeatCustomEvents.RemoveAt(waveSetting.waveRepeatCustomEvents.Count - 1);
                            }
                            GUI.contentColor = Color.white;

                            EditorGUILayout.EndHorizontal();

                            if (waveSetting.waveRepeatCustomEvents.Count == 0) {
                                DTInspectorUtility.ShowColorWarningBox("You have no Custom Events selected to fire.");
                            }

                            DTInspectorUtility.VerticalSpace(2);

                            // ReSharper disable once ForCanBeConvertedToForeach
                            for (var i = 0; i < waveSetting.waveRepeatCustomEvents.Count; i++) {
                                var anEvent = waveSetting.waveRepeatCustomEvents[i].CustomEventName;

                                anEvent = DTInspectorUtility.SelectCustomEventForVariable(ref _isDirty, anEvent,
                                    _settings, "Custom Event");

                                if (anEvent == waveSetting.waveRepeatCustomEvents[i].CustomEventName) {
                                    continue;
                                }

                                waveSetting.waveRepeatCustomEvents[i].CustomEventName = anEvent;
                            }
                        }
                        EditorGUILayout.EndVertical();
                        if (waveSetting.waveRepeatCustomEvents.Count > 0) {
                            DTInspectorUtility.VerticalSpace(2);
                        }
                    }
                    EditorGUILayout.EndToggleGroup();
                    DTInspectorUtility.AddSpaceForNonU5();

                    EditorGUI.indentLevel = 0;
                    // show randomizations
                    const string variantTag = " Randomization";

                    DTInspectorUtility.StartGroupHeader(1);
                    newExpanded = EditorGUILayout.BeginToggleGroup(variantTag, waveSetting.enableRandomizations);
                    if (newExpanded != waveSetting.enableRandomizations) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Randomization");
                        waveSetting.enableRandomizations = newExpanded;
                        showVisualization = true;
                    }
                    DTInspectorUtility.EndGroupHeader();
                    if (waveSetting.enableRandomizations) {
                        EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(100));
                        EditorGUILayout.LabelField("Random Rotation");

                        var newRandX = GUILayout.Toggle(waveSetting.randomXRotation, "X");
                        if (newRandX != waveSetting.randomXRotation) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Random Rotation X");
                            waveSetting.randomXRotation = newRandX;
                            showVisualization = true;
                        }
                        GUILayout.Space(10);

                        var newRandY = GUILayout.Toggle(waveSetting.randomYRotation, "Y");
                        if (newRandY != waveSetting.randomYRotation) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Random Rotation Y");
                            waveSetting.randomYRotation = newRandY;
                            showVisualization = true;
                        }
                        GUILayout.Space(10);

                        var newRandZ = GUILayout.Toggle(waveSetting.randomZRotation, "Z");
                        if (newRandZ != waveSetting.randomZRotation) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Random Rotation Z");
                            waveSetting.randomZRotation = newRandZ;
                            showVisualization = true;
                        }
                        EditorGUILayout.EndHorizontal();

                        if (waveSetting.randomXRotation) {
                            var randX = waveSetting.randomXRotMin.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomXRotMin, "Rand. X Rot. Min", _settings);
                            if (randX != waveSetting.randomXRotMin.Value) {
                                showVisualization = true;
                            }

                            randX = waveSetting.randomXRotMax.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomXRotMax, "Rand. X Rot. Max", _settings);
                            if (randX != waveSetting.randomXRotMax.Value) {
                                showVisualization = true;
                            }
                        }

                        if (waveSetting.randomYRotation) {
                            var randY = waveSetting.randomYRotMin.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomYRotMin, "Rand. Y Rot. Min", _settings);
                            if (randY != waveSetting.randomYRotMin.Value) {
                                showVisualization = true;
                            }

                            randY = waveSetting.randomYRotMax.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomYRotMax, "Rand. Y Rot. Max", _settings);
                            if (randY != waveSetting.randomYRotMax.Value) {
                                showVisualization = true;
                            }
                        }

                        if (waveSetting.randomZRotation) {
                            var randZ = waveSetting.randomZRotMin.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomZRotMin, "Rand. Z Rot. Min", _settings);
                            if (randZ != waveSetting.randomZRotMin.Value) {
                                showVisualization = true;
                            }

                            randZ = waveSetting.randomZRotMax.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomZRotMax, "Rand. Z Rot. Max", _settings);
                            if (randZ != waveSetting.randomZRotMax.Value) {
                                showVisualization = true;
                            }
                        }

                        EditorGUILayout.Separator();

                        var rndX = waveSetting.randomDistX.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomDistX, "Rand. Distance X", _settings);
                        if (rndX != waveSetting.randomDistX.Value) {
                            showVisualization = true;
                        }

                        var rndY = waveSetting.randomDistY.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomDistY, "Rand. Distance Y", _settings);
                        if (rndY != waveSetting.randomDistY.Value) {
                            showVisualization = true;
                        }

                        var rndZ = waveSetting.randomDistZ.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.randomDistZ, "Rand. Distance Z", _settings);
                        if (rndZ != waveSetting.randomDistZ.Value) {
                            showVisualization = true;
                        }
                    }
                    EditorGUILayout.EndToggleGroup();
                    DTInspectorUtility.AddSpaceForNonU5();

                    // show increments
                    var incTag = " Incremental Settings";
                    DTInspectorUtility.StartGroupHeader(1);
                    newExpanded = EditorGUILayout.BeginToggleGroup(incTag, waveSetting.enableIncrements);
                    if (newExpanded != waveSetting.enableIncrements) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Incremental Settings");
                        waveSetting.enableIncrements = newExpanded;
                        showVisualization = true;
                    }
                    DTInspectorUtility.EndGroupHeader();
                    if (waveSetting.enableIncrements) {
                        var oldX = waveSetting.incrementPositionX.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.incrementPositionX, "Distance X", _settings);
                        if (oldX != waveSetting.incrementPositionX.Value) {
                            showVisualization = true;
                        }

                        var oldY = waveSetting.incrementPositionY.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.incrementPositionY, "Distance Y", _settings);
                        if (oldY != waveSetting.incrementPositionY.Value) {
                            showVisualization = true;
                        }

                        var oldZ = waveSetting.incrementPositionZ.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.incrementPositionZ, "Distance Z", _settings);
                        if (oldZ != waveSetting.incrementPositionZ.Value) {
                            showVisualization = true;
                        }

                        EditorGUILayout.Separator();

                        if (waveSetting.enableRandomizations && waveSetting.randomXRotation) {
                            DTInspectorUtility.ShowColorWarningBox("Rotation X - cannot be used with Random Rotation X.");
                        } else {
                            var rotX = waveSetting.incrementRotX.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.incrementRotX, "Rotation X", _settings);
                            if (rotX != waveSetting.incrementRotX.Value) {
                                showVisualization = true;
                            }
                        }

                        if (waveSetting.enableRandomizations && waveSetting.randomYRotation) {
                            DTInspectorUtility.ShowColorWarningBox("Rotation Y - cannot be used with Random Rotation Y.");
                        } else {
                            var rotY = waveSetting.incrementRotY.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.incrementRotY, "Rotation Y", _settings);
                            if (rotY != waveSetting.incrementRotY.Value) {
                                showVisualization = true;
                            }
                        }

                        if (waveSetting.enableRandomizations && waveSetting.randomZRotation) {
                            DTInspectorUtility.ShowColorWarningBox("Rotation Z - cannot be used with Random Rotation Z.");
                        } else {
                            var rotZ = waveSetting.incrementRotZ.Value;
                            KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.incrementRotZ, "Rotation Z", _settings);
                            if (rotZ != waveSetting.incrementRotZ.Value) {
                                showVisualization = true;
                            }
                        }

                        var newIncKc = EditorGUILayout.Toggle("Keep Center", waveSetting.enableKeepCenter);
                        if (newIncKc != waveSetting.enableKeepCenter) {
                            UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Keep Center");
                            waveSetting.enableKeepCenter = newIncKc;
                            showVisualization = true;
                        }
                    }
                    EditorGUILayout.EndToggleGroup();

                    DTInspectorUtility.AddSpaceForNonU5();

                    // show increments
                    incTag = " Post-spawn Nudge Settings";
                    DTInspectorUtility.StartGroupHeader(1);
                    newExpanded = EditorGUILayout.BeginToggleGroup(incTag, waveSetting.enablePostSpawnNudge);
                    if (newExpanded != waveSetting.enablePostSpawnNudge) {
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "toggle Post-spawn Nudge Settings");
                        waveSetting.enablePostSpawnNudge = newExpanded;
                        showVisualization = true;
                    }
                    DTInspectorUtility.EndGroupHeader();
                    if (waveSetting.enablePostSpawnNudge) {
                        var oldF = waveSetting.postSpawnNudgeFwd.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.postSpawnNudgeFwd, "Nudge Forward", _settings);
                        if (oldF != waveSetting.postSpawnNudgeFwd.Value) {
                            showVisualization = true;
                        }

                        var oldR = waveSetting.postSpawnNudgeRgt.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.postSpawnNudgeRgt, "Nudge Right", _settings);
                        if (oldR != waveSetting.postSpawnNudgeRgt.Value) {
                            showVisualization = true;
                        }

                        var oldD = waveSetting.postSpawnNudgeDwn.Value;
                        KillerVariablesHelper.DisplayKillerFloat(ref _isDirty, waveSetting.postSpawnNudgeDwn, "Nudge Down", _settings);
                        if (oldD != waveSetting.postSpawnNudgeDwn.Value) {
                            showVisualization = true;
                        }
                    }
                    EditorGUILayout.EndToggleGroup();

                    EditorGUILayout.EndToggleGroup();
                    EditorGUILayout.EndVertical();

                    DTInspectorUtility.VerticalSpace(3);

                    // ReSharper disable once InvertIf
                    if (showVisualization || (waveEnabled && waveSetting.visualizeWave) || (waveActivated && waveSetting.visualizeWave && waveSetting.enableWave)) {
                        _settings.gameObject.DestroyChildrenImmediate();
                        _settings.SpawnWaveVisual(waveSetting);
                    }
                }

                if (waveToDelete != null) {
                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "delete Wave");
                    _settings.waveSpecs.Remove(waveToDelete);

                    if (!Application.isPlaying) {
                        _settings.gameObject.DestroyChildrenImmediate();
                    }
                }

                if (waveToInsertAt > -1) {
                    if (levelSettings.LevelTimes.Count == 0) {
                        DTInspectorUtility.ShowAlert("You will not have any Level or Wave #'s to select in your Spawner Wave Settings until you add a Level in LevelSettings. Please do that first.");
                    } else {
                        var newWave = new WaveSpecifics();
                        UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "add Wave");
                        _settings.waveSpecs.Insert(waveToInsertAt + 1, newWave);
                    }
                }

                if (waveToMoveUp.HasValue) {
                    var item = _settings.waveSpecs[waveToMoveUp.Value];
                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "shift up Wave");
                    _settings.waveSpecs.Insert(waveToMoveUp.Value - 1, item);
                    _settings.waveSpecs.RemoveAt(waveToMoveUp.Value + 1);
                }

                if (waveToMoveDown.HasValue) {
                    var index = waveToMoveDown.Value + 1;

                    var item = _settings.waveSpecs[index];
                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "shift down Wave");
                    _settings.waveSpecs.Insert(index - 1, item);
                    _settings.waveSpecs.RemoveAt(index + 1);
                }

                if (waveToClone.HasValue) {
                    var index = waveToClone.Value;

                    var newItem = _settings.waveSpecs[index].Clone();
                    UndoHelper.RecordObjectPropertyForUndo(ref _isDirty, _settings, "clone Wave");
                    _settings.waveSpecs.Insert(index, newItem);
                }

                DTInspectorUtility.EndGroupedControls();
            }
        } else {
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndHorizontal();
        }

        if (GUI.changed || _isDirty) {
            EditorUtility.SetDirty(target);	// or it won't save the data!!
        }

        //DrawDefaultInspector();
    }
 /// <summary>
 /// This method gets called when an elimination wave has the last item despawned, on the first and every repeat.
 /// </summary>
 /// <param name="spec">The wave specifics.</param>
 public virtual void EliminationWaveCompleted(WaveSpecifics spec)
 {
     // called at the end of each wave, whether or not it is repeating. This is called before the Repeat delay
     // Please do not manipulate values in the "spec". It is for your read-only information
 }