/// <summary> /// The scripting randomization will be complete at this stage, so access to which levels /// are available, which are unarmed, ammoless etc is accurate at this point. The list of /// levels is always in the original sequence when accessed here, but can be sorted by level /// sequence if it's preferred to add an additional randomization factor. The save monitor /// is used to report progress and checks should be performed regularly on IsCancelled. /// </summary> protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMonitor monitor) { List <TR23ScriptedLevel> levels = new List <TR23ScriptedLevel> ( scriptEditor.ScriptedLevels.Cast <TR23ScriptedLevel>().ToList() ); // Optionally sort based on randomized sequencing. Perhaps this should be an option for users? /*levels.Sort(delegate (TR23ScriptedLevel lvl1, TR23ScriptedLevel lvl2) * { * return lvl1.Sequence.CompareTo(lvl2.Sequence); * });*/ string wipDirectory = _io.WIPOutputDirectory.FullName; if (!monitor.IsCancelled && RandomizeSecrets) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing secrets"); new SecretReplacer { AllowHard = HardSecrets, Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, IsDevelopmentModeOn = DevelopmentMode }.Randomize(SecretSeed); } if (!monitor.IsCancelled && RandomizeItems) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing items"); new ItemRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, IncludeKeyItems = IncludeKeyItems, IsDevelopmentModeOn = DevelopmentMode }.Randomize(ItemSeed); } if (!monitor.IsCancelled && RandomizeEnemies) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing enemies"); new EnemyRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor }.Randomize(EnemySeed); } if (!monitor.IsCancelled && RandomizeTextures) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing textures"); new TextureRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor }.Randomize(TextureSeed); } }
/// <summary> /// The scripting randomization will be complete at this stage, so access to which levels /// are available, which are unarmed, ammoless etc is accurate at this point. The list of /// levels is always in the original sequence when accessed here, but can be sorted by level /// sequence if it's preferred to add an additional randomization factor. The save monitor /// is used to report progress and checks should be performed regularly on IsCancelled. /// </summary> protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMonitor monitor) { List <TR23ScriptedLevel> levels = new List <TR23ScriptedLevel> ( scriptEditor.EnabledScriptedLevels.Cast <TR23ScriptedLevel>().ToList() ); if (scriptEditor.GymAvailable) { levels.Add(scriptEditor.AssaultLevel as TR23ScriptedLevel); } // Optionally sort based on randomized sequencing. Perhaps this should be an option for users? /*levels.Sort(delegate (TR23ScriptedLevel lvl1, TR23ScriptedLevel lvl2) * { * return lvl1.Sequence.CompareTo(lvl2.Sequence); * });*/ string wipDirectory = _io.WIPOutputDirectory.FullName; // Texture monitoring is needed between enemy and texture randomization // to track where imported enemies are placed. using (TexturePositionMonitorBroker textureMonitor = new TexturePositionMonitorBroker()) { if (!monitor.IsCancelled && RandomizeSecrets) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing secrets"); new SecretReplacer { AllowHard = HardSecrets, AllowGlitched = GlitchedSecrets, Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, IsDevelopmentModeOn = DevelopmentMode }.Randomize(SecretSeed); } if (!monitor.IsCancelled && RandomizeAudio) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing audio tracks"); new AudioRandomizer { ScriptEditor = scriptEditor as TR23ScriptEditor, Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, ChangeTriggerTracks = ChangeTriggerTracks }.Randomize(AudioSeed); } if (!monitor.IsCancelled && DeduplicateTextures) { // This is needed to make as much space as possible available for cross-level enemies. // We do this if we are implementing cross-level enemies OR if randomizing textures, // as the texture mapping is optimised for levels that have been deduplicated. monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Deduplicating textures"); new TextureDeduplicator { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor }.Deduplicate(); } if (!monitor.IsCancelled && RandomizeNightMode) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing night mode"); new NightModeRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, NumLevels = NightModeCount }.Randomize(NightModeSeed); } ItemRandomizer itemRandomizer = null; if (!monitor.IsCancelled && RandomizeItems) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, string.Format("Randomizing standard{0} items", IncludeKeyItems ? " and key" : string.Empty)); (itemRandomizer = new ItemRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, IncludeKeyItems = IncludeKeyItems, PerformEnemyWeighting = RandomizeEnemies && CrossLevelEnemies, TextureMonitor = textureMonitor, IsDevelopmentModeOn = DevelopmentMode }).Randomize(ItemSeed); } if (!monitor.IsCancelled && RandomizeEnemies) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing enemies"); new EnemyRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, CrossLevelEnemies = CrossLevelEnemies, ProtectMonks = ProtectMonks, DocileBirdMonsters = DocileBirdMonsters, TextureMonitor = textureMonitor }.Randomize(EnemySeed); } // Randomize ammo/weapon in unarmed levels post enemy randomization if (!monitor.IsCancelled && RandomizeItems) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing unarmed level items"); itemRandomizer.RandomizeAmmo(); } if (!monitor.IsCancelled && RandomizeStartPosition) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing start positions"); new StartPositionRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, RotateOnly = RotateStartPositionOnly, DevelopmentMode = DevelopmentMode }.Randomize(StartPositionSeed); } if (!monitor.IsCancelled && RandomizeOutfits) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing outfits"); new OutfitRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, PersistOutfits = PersistOutfits, RandomlyCutHair = RandomlyCutHair, TextureMonitor = textureMonitor }.Randomize(OutfitSeed); } if (!monitor.IsCancelled) { if (RandomizeTextures) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing textures"); new TextureRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, PersistVariants = PersistTextureVariants, RetainKeySprites = RetainKeySpriteTextures, RetainSecretSprites = RetainSecretSpriteTextures, NightModeOnly = !RandomizeTextures, TextureMonitor = textureMonitor }.Randomize(TextureSeed); } else if (RandomizeNightMode) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Randomizing night mode textures"); new TextureRandomizer { Levels = levels, BasePath = wipDirectory, SaveMonitor = monitor, NightModeOnly = true, TextureMonitor = textureMonitor }.Randomize(NightModeSeed); } } } }