diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d6878998..d3848b52e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,12 @@ - added an option to control whether or not key items can be placed in locations that rely on return paths (#474) - added an option to control key item continuity e.g. The Seraph being placed as an item in Barkhang Monastery if The Deck hasn't been visited (#474) - added item randomization to Home Sweet Home, provided the level starts with weapons and ammo already (#474) +- added support for TR1X 3.0, including randomized enemy item drops (#572) - fixed spelling mistakes in TR1 French gamestring localization (#560) - fixed a key item softlock in Diving Area (#564) - improved changelog, readme and contributing documentation - improved regular item, key item, and secret item location generation and selection in TR1-3 (#474) +- removed TombATI support (#572) - replaced purist mode with explicit options for adding return paths, fixing OG bugs, and fixing shortcuts (#563) ## [V1.7.3](https://github.com/LostArtefacts/TR-Rando/compare/V1.7.2...V1.7.3) - 2023-09-30 diff --git a/Deps/TRGE.Coord.dll b/Deps/TRGE.Coord.dll index 853206ebb..34a1573df 100644 Binary files a/Deps/TRGE.Coord.dll and b/Deps/TRGE.Coord.dll differ diff --git a/Deps/TRGE.Core.dll b/Deps/TRGE.Core.dll index 3b2c743df..788231faf 100644 Binary files a/Deps/TRGE.Core.dll and b/Deps/TRGE.Core.dll differ diff --git a/README.md b/README.md index 90f2c6540..753f6c80c 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ For keeping track of levels while you play, check out the [TRRandoTracker](https * Trxye - Lots of ideas and development help. * Towandaa - Lots of ideas and contributions, including secret, vehicle and sprite randomization. * Leoc1995 - For TR1 puzzle inspiration and contributing enemy variants from his [TRLEs](https://trcustoms.org/users/854). -* Dash and Walkawayy - For providing great support and rando-related additions to [Tomb1Main](https://github.com/LostArtefacts/Tomb1Main). +* Dash and Walkawayy - For providing great support and rando-related additions to [TR1X](https://github.com/LostArtefacts/TR1X). * Jimmy Beon - Providing us with an excellent assortment of custom textures for use in rando. * Topixtor - For endless testing to fix issues such as the opera house/barkhang entity freeze and mirrored levels, and providing enemy variations from his [TRLEs](https://trcustoms.org/users/927). * Radgryd - For endless TR1 testing and lots of great ideas for the rando. diff --git a/Resources/Documentation/ENEMIES.md b/Resources/Documentation/ENEMIES.md index 58889f54b..78af06ced 100644 --- a/Resources/Documentation/ENEMIES.md +++ b/Resources/Documentation/ENEMIES.md @@ -20,17 +20,15 @@ The following enemy restrictions are in place when using "Default" restriction m Torso is restricted to appear only in certain rooms where there is a little more space to fight. He will always appear in Great Pyramid in the usual place to activate the door, unless `Replace required enemies` is enabled. In this case, an alternative way to open the door will be provided. ### Pierre -Pierre will always be killable, as allocating runaway Pierres using current enemy positioning is awkward, given that when they spawn they deactivate the previous one (or with TombATI the newly triggered one doesn't replace the existing). Pierre will also always drop the scion, magnums and a key. If Pierre is not present at the end of Tihocan, a key will be added to pickup (this location will be randomized if key item randomization is enabled). +Pierre will always be killable, as allocating runaway Pierres using current enemy positioning is awkward, given that when they spawn they deactivate the previous one. Where Pierre is needed at the end of Tomb of Tihocan, there are different outcomes depending on which settings you have enabled. +- If key item randomization is enabled, then a key and the scion will be added as physical items to collect, and their location will be randomized. +- If key item randomization is not enabled: + - Whichever enemy is in Pierre's place will drop the key items, if that enemy is capable of dropping items. + - Otherwise, physical pickups are added per above, but their locations are fixed. ### Larson By default, Larson will end the level in Tomb of Qualopec when he is killed. This can be disabled by selecting `Replace required enemies` in the enemy options window. In this case, an alternative level ending will be added to this level. If this option is *not* enabled, the single default Larson will appear when `Default` restrictions mode is selected; in `No restrictions` mode, he can appear anywhere in the level, and the level will end wherever he is killed. -### SkateboardKid -SkateboardKid is restricted to only appear in Natla's Mines, *unless Tomb1Main is being used*. This is because of an OG bug that can cause a crash, which is present in TombATI but is fixed in Tomb1Main. - -### Natla -Natla is restricted to only appear in Great Pyramid, *unless Tomb1Main is being used*. This is because of an OG bug that can cause a crash, which is present in TombATI but is fixed in Tomb1Main. - ### Atlanteans Collectively, there are some limits on Atlanteans per level to avoid extremely difficult areas. There will be a maximum grouping count for the following levels of any of the following enemies. * Centaur/Centaur Statue @@ -57,18 +55,16 @@ Collectively, there are some limits on Atlanteans per level to avoid extremely d Note that mummies can appear as flying mutants in other levels. This model is present in City of Khamoon but is unused in the original game. ### Bacon Lara -Bacon Lara will always appear in her OG room in Atlantis, *unless the level is mirrored and TombATI is being used*. In this case, an alternative way to open the door will be provided. +Bacon Lara will always appear in her OG room in Atlantis. She may also appear in other levels if puzzle/challenge room randomization is enabled. ## No Restrictions Mode -In Tomb1Main, "No Restrictions" mode will switch off each of the above restrictions, apart from Pierre always being killable. - -In TombATI, the following restrictions will always remain: -* Pierre always being killable -* SkateboardKid appearing in Natla's Mines only -* Natla appearing in Great Pyramid only +"No Restrictions" mode will switch off each of the above restrictions, apart from Pierre always being killable. This mode will also allow land enemies to appear underwater as the engine does not kill them (as in TR2 onwards). +## Item drops +In the original TR1, Pierre, Cowboy, Skateboard Kid and Kold are hard-coded to drop items when they are killed. This is not the case in the randomizer if enemy randomization is enabled. There does however remain a chance for items to be allocated to _any_ suitable enemy type, so make sure to keep your eyes peeled for pickups. See https://github.com/LostArtefacts/TR1X/blob/stable/GAMEFLOW.md#item-drops for more information. + ## Enemy Types Some levels have their total enemy types count increased for more variety. This is a maximum rather than a guaranteed number of types (e.g. for Caves, 5 models will always be imported, but no checks are performed to ensure at least one of each is assigned to entities). Levels in _italics_ remain unchanged. diff --git a/Resources/Documentation/SECRETS.md b/Resources/Documentation/SECRETS.md index c8c938fff..c528b9cd0 100644 --- a/Resources/Documentation/SECRETS.md +++ b/Resources/Documentation/SECRETS.md @@ -39,7 +39,7 @@ The artefact types to collect will change per level. This is in place to ensure | Pyramid | Gold Idol | ### Secret Count -If [Tomb1Main](https://github.com/rr-/Tomb1Main) is being used, the number of secrets to collect per level can be changed. When you start a level, check Lara's compass to find out how many secrets you need to collect. +If [TR1X](https://github.com/LostArtefacts/TR1X) is being used, the number of secrets to collect per level can be changed. When you start a level, check Lara's compass to find out how many secrets you need to collect. The options are: diff --git a/Resources/trview/plugins/key_items/plugin.lua b/Resources/trview/plugins/key_items/plugin.lua index 9bc1bb54c..c29d7be4b 100644 --- a/Resources/trview/plugins/key_items/plugin.lua +++ b/Resources/trview/plugins/key_items/plugin.lua @@ -43,6 +43,7 @@ m_KeyNames[1][18389] = "K1 Gold Key (Pierre)" m_KeyNames[1][18133] = "K1 Gold Key (flip map)" m_KeyNames[1][18277] = "K2 Rusty Key (boulders)" m_KeyNames[1][18267] = "K2 Rusty Key (clang-clang)" +m_KeyNames[1][18444] = "Scion (Pierre)" m_KeyNames[1][19193] = "K1 Sapphire Key (end)" m_KeyNames[1][19217] = "K1 Sapphire Key (start)" m_KeyNames[1][20213] = "K1 Sapphire Key (alt ending)" diff --git a/TRLevelControl/Helpers/TR1TypeUtilities.cs b/TRLevelControl/Helpers/TR1TypeUtilities.cs index 29d695482..91d23f535 100644 --- a/TRLevelControl/Helpers/TR1TypeUtilities.cs +++ b/TRLevelControl/Helpers/TR1TypeUtilities.cs @@ -408,6 +408,12 @@ public static List GetAtlanteanEggEnemies() }; } + public static bool IsEggType(TR1Type type) + { + return type == TR1Type.AtlanteanEgg + || type == TR1Type.AdamEgg; + } + public static List GetSwitchTypes() { return new() diff --git a/TRLevelControl/Model/Base/Enums/TR1Type.cs b/TRLevelControl/Model/Base/Enums/TR1Type.cs index f94c0a881..2d433baa3 100644 --- a/TRLevelControl/Model/Base/Enums/TR1Type.cs +++ b/TRLevelControl/Model/Base/Enums/TR1Type.cs @@ -314,6 +314,7 @@ public enum TR1Type Tihocan_K1_GoldKeyPierre = 18389, Tihocan_K2_RustyKeyBoulders = 18277, Tihocan_K2_RustyKeyClangClang = 18267, + Tihocan_Scion_EndRoom = 18444, KhamoonKeyItemBase = 19000, Khamoon_K1_SapphireKeyEnd = 19193, diff --git a/TRRandomizerCore/Editors/TR1RandoEditor.cs b/TRRandomizerCore/Editors/TR1RandoEditor.cs index 710873f7a..193c6534c 100644 --- a/TRRandomizerCore/Editors/TR1RandoEditor.cs +++ b/TRRandomizerCore/Editors/TR1RandoEditor.cs @@ -39,11 +39,8 @@ protected override int GetSaveTarget(int numLevels) // String rando always runs target++; - if (_edition.IsCommunityPatch) - { - // Injection checks - target += numLevels; - } + // Injection checks + target += numLevels; if (Settings.RandomizeStartingHealth) { @@ -123,15 +120,11 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni string backupDirectory = _io.BackupDirectory.FullName; string wipDirectory = _io.WIPOutputDirectory.FullName; - bool isTomb1Main = scriptEditor.Edition.IsCommunityPatch; - if (isTomb1Main) + TR1ScriptEditor scriptEd = scriptEditor as TR1ScriptEditor; + if (Settings.DevelopmentMode) { - TR1ScriptEditor scriptEd = scriptEditor as TR1ScriptEditor; - if (Settings.DevelopmentMode) - { - scriptEd.EnableCheats = true; - } - + scriptEd.EnableCheats = true; + scriptEd.EnableConsole = true; scriptEditor.SaveScript(); } @@ -177,7 +170,7 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni }.Randomize(Settings.GameStringsSeed); } - if (!monitor.IsCancelled && isTomb1Main) + if (!monitor.IsCancelled) { monitor.FireSaveStateBeginning(TRSaveCategory.Custom, "Validating data injections"); new TR1InjectionProcessor @@ -350,10 +343,7 @@ protected override void SaveImpl(AbstractTRScriptEditor scriptEditor, TRSaveMoni } } - if (isTomb1Main) - { - AmendTitleAndCredits(scriptEditor, monitor); - } + AmendTitleAndCredits(scriptEditor, monitor); } private void AmendTitleAndCredits(AbstractTRScriptEditor scriptEditor, TRSaveMonitor monitor) diff --git a/TRRandomizerCore/Helpers/T1MInjectionType.cs b/TRRandomizerCore/Helpers/TR1XInjectionType.cs similarity index 85% rename from TRRandomizerCore/Helpers/T1MInjectionType.cs rename to TRRandomizerCore/Helpers/TR1XInjectionType.cs index a5115a0e5..cf4d73541 100644 --- a/TRRandomizerCore/Helpers/T1MInjectionType.cs +++ b/TRRandomizerCore/Helpers/TR1XInjectionType.cs @@ -1,6 +1,6 @@ namespace TRRandomizerCore.Helpers; -public enum T1MInjectionType +public enum TR1XInjectionType { General, Braid, diff --git a/TRRandomizerCore/Levels/TR1CombinedLevel.cs b/TRRandomizerCore/Levels/TR1CombinedLevel.cs index e9fafe05c..4ac5b6748 100644 --- a/TRRandomizerCore/Levels/TR1CombinedLevel.cs +++ b/TRRandomizerCore/Levels/TR1CombinedLevel.cs @@ -14,7 +14,7 @@ public class TR1CombinedLevel public TR1Level Data { get; set; } /// - /// The scripting information for the level stored in Tomb1Main_gameflow.json5. + /// The scripting information for the level stored in TR1X_gameflow.json5. /// public TR1ScriptedLevel Script { get; set; } diff --git a/TRRandomizerCore/Processors/TR1/TR1InjectionProcessor.cs b/TRRandomizerCore/Processors/TR1/TR1InjectionProcessor.cs index 2e248b56b..742acf8b3 100644 --- a/TRRandomizerCore/Processors/TR1/TR1InjectionProcessor.cs +++ b/TRRandomizerCore/Processors/TR1/TR1InjectionProcessor.cs @@ -6,27 +6,27 @@ namespace TRRandomizerCore.Processors; public class TR1InjectionProcessor : TR1LevelProcessor { - private static readonly uint _t1mMagic = 'T' | ('1' << 8) | ('M' << 16) | ('J' << 24); - private static readonly Version _minT1MVersion = new(2, 15); - private static readonly List _permittedInjections = new() + private static readonly uint _tr1xMagic = 'T' | ('1' << 8) | ('M' << 16) | ('J' << 24); + private static readonly Version _minTR1XVersion = new(3, 0); + private static readonly List _permittedInjections = new() { - T1MInjectionType.LaraAnims, - T1MInjectionType.LaraJumps, + TR1XInjectionType.LaraAnims, + TR1XInjectionType.LaraJumps, }; public void Run() { TR1Script script = ScriptEditor.Script as TR1Script; - bool t1mVersionSupported = script.Edition.ExeVersion != null - && script.Edition.ExeVersion >= _minT1MVersion; - script.Injections = t1mVersionSupported ? + bool tr1xVersionSupported = script.Edition.ExeVersion != null + && script.Edition.ExeVersion >= _minTR1XVersion; + script.Injections = tr1xVersionSupported ? GetSupportedInjections(script.Injections) : null; foreach (TR1ScriptedLevel level in Levels) { LoadLevelInstance(level); - AdjustInjections(_levelInstance, t1mVersionSupported); + AdjustInjections(_levelInstance, tr1xVersionSupported); SaveLevelInstance(); if (!TriggerProgress()) @@ -38,9 +38,9 @@ public void Run() SaveScript(); } - private void AdjustInjections(TR1CombinedLevel level, bool t1mVersionSupported) + private void AdjustInjections(TR1CombinedLevel level, bool tr1xVersionSupported) { - if (!t1mVersionSupported) + if (!tr1xVersionSupported) { level.Script.ResetInjections(); return; @@ -72,13 +72,13 @@ private string[] GetSupportedInjections(string[] injections) } using BinaryReader reader = new(File.OpenRead(path)); - if (reader.ReadUInt32() != _t1mMagic) + if (reader.ReadUInt32() != _tr1xMagic) { continue; } reader.ReadUInt32(); // Skip version - if (_permittedInjections.Contains((T1MInjectionType)reader.ReadUInt32())) + if (_permittedInjections.Contains((TR1XInjectionType)reader.ReadUInt32())) { validInjections.Add(injection); } diff --git a/TRRandomizerCore/Randomizers/Shared/LocationPicker.cs b/TRRandomizerCore/Randomizers/Shared/LocationPicker.cs index 472327d61..7784bc6ea 100644 --- a/TRRandomizerCore/Randomizers/Shared/LocationPicker.cs +++ b/TRRandomizerCore/Randomizers/Shared/LocationPicker.cs @@ -19,7 +19,7 @@ public class LocationPicker : IRouteManager private Random _generator; public Func TriggerTestAction { get; set; } - public Func KeyItemTestAction { get; set; } + public Func KeyItemTestAction { get; set; } public List RoomInfos { get; set; } public int LevelSize { get; private set; } @@ -140,7 +140,7 @@ public Location GetKeyItemLocation(int keyItemID, TREntity entity, bool ha continue; } - if (KeyItemTestAction != null && !KeyItemTestAction(newLocation)) + if (KeyItemTestAction != null && !KeyItemTestAction(newLocation, hasPickupTrigger)) { continue; } diff --git a/TRRandomizerCore/Randomizers/TR1/TR1AudioRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1AudioRandomizer.cs index 39a3e1b08..eb0d6cff7 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1AudioRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1AudioRandomizer.cs @@ -297,13 +297,12 @@ private static short ImportSoundEffect(TR1Level level, TR1SFXDefinition definiti private void ImportSpeechSFX(TR1CombinedLevel level) { - if (!ScriptEditor.Edition.IsCommunityPatch - || !(ScriptEditor as TR1ScriptEditor).FixSpeechesKillingMusic) + if (!(ScriptEditor as TR1ScriptEditor).FixSpeechesKillingMusic) { return; } - // T1M can play enemy speeches as SFX to avoid killing the current + // TR1X can play enemy speeches as SFX to avoid killing the current // track, so ensure that the required data is in the level if any // of these are used on the floor. @@ -352,11 +351,8 @@ private void RandomizeWibble(TR1CombinedLevel level) details.Wibble = true; } - if (ScriptEditor.Edition.IsCommunityPatch) - { - (ScriptEditor.Script as TR1Script).EnablePitchedSounds = true; - ScriptEditor.SaveScript(); - } + (ScriptEditor as TR1ScriptEditor).EnablePitchedSounds = true; + ScriptEditor.SaveScript(); } } } diff --git a/TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs index f3695192f..9ff4535c5 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1EnemyRandomizer.cs @@ -52,9 +52,11 @@ public override void Randomize(int seed) RandomizeExistingEnemies(); } - if (ScriptEditor.Edition.IsCommunityPatch) + TR1Script script = ScriptEditor.Script as TR1Script; + script.DisableTrexCollision = true; + if (Settings.UseRecommendedCommunitySettings) { - (ScriptEditor.Script as TR1Script).DisableTrexCollision = true; + script.ConvertDroppedGuns = true; } } @@ -239,14 +241,13 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR1CombinedLevel level) // looping infinitely if it's not possible to fill to capacity ISet testedEntities = new HashSet(); List eggEntities = TR1TypeUtilities.GetAtlanteanEggEnemies(); - bool isTomb1Main = ScriptEditor.Edition.IsCommunityPatch; while (newEntities.Count < newEntities.Capacity && testedEntities.Count < allEnemies.Count) { TR1Type entity = allEnemies[_generator.Next(0, allEnemies.Count)]; testedEntities.Add(entity); // Make sure this isn't known to be unsupported in the level - if (!TR1EnemyUtilities.IsEnemySupported(level.Name, entity, difficulty, isTomb1Main)) + if (!TR1EnemyUtilities.IsEnemySupported(level.Name, entity, difficulty)) { continue; } @@ -323,7 +324,7 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR1CombinedLevel level) // Make sure we have an unrestricted enemy available for the individual level conditions. This will // guarantee a "safe" enemy for the level; we avoid aliases here to avoid further complication. bool RestrictionCheck(TR1Type e) => - !TR1EnemyUtilities.IsEnemySupported(level.Name, e, difficulty, isTomb1Main) + !TR1EnemyUtilities.IsEnemySupported(level.Name, e, difficulty) || newEntities.Contains(e) || TR1TypeUtilities.IsWaterCreature(e) || TR1EnemyUtilities.IsEnemyRestricted(level.Name, e, difficulty) @@ -380,7 +381,7 @@ private static List GetCurrentEnemyEntities(TR1CombinedLevel level) private TR1Type SelectRequiredEnemy(List pool, TR1CombinedLevel level, RandoDifficulty difficulty) { - pool.RemoveAll(e => !TR1EnemyUtilities.IsEnemySupported(level.Name, e, difficulty, ScriptEditor.Edition.IsCommunityPatch)); + pool.RemoveAll(e => !TR1EnemyUtilities.IsEnemySupported(level.Name, e, difficulty)); TR1Type entity; if (pool.All(_excludedEnemies.Contains)) @@ -445,6 +446,9 @@ private void RandomizeEnemies(TR1CombinedLevel level, EnemyRandomizationCollecti { AmendAtlanteanModels(level, enemies); + // Clear all default enemy item drops + level.Script.ItemDrops.Clear(); + // Get a list of current enemy entities List allEnemies = TR1TypeUtilities.GetFullListOfEnemies(); List enemyEntities = level.Data.Entities.FindAll(e => allEnemies.Contains(e.TypeID)); @@ -646,7 +650,7 @@ private void RandomizeEnemies(TR1CombinedLevel level, EnemyRandomizationCollecti } } - currentEntity.CodeBits = AtlanteanToCodeBits(spawnType); + currentEntity.CodeBits = TR1EnemyUtilities.AtlanteanToCodeBits(spawnType); if (eggLocation != null) { currentEntity.X = eggLocation.X; @@ -683,18 +687,6 @@ private void RandomizeEnemies(TR1CombinedLevel level, EnemyRandomizationCollecti currentEntity.Invisible = true; } - if (newEntityType == TR1Type.Pierre) - { - // Pierre will always be OneShot (see SetEntityTriggers) for the time being, because placement - // of runaway Pierres is awkward - only one can be active at a time. - - // He is hard-coded to drop Key1, so add a string if this level doesn't have one. - if (ScriptEditor.Edition.IsCommunityPatch && level.Script.Keys.Count == 0) - { - level.Script.Keys.Add("Pierre's Spare Key"); - } - } - // Make sure to convert back to the actual type currentEntity.TypeID = TR1TypeUtilities.TranslateAlias(newEntityType); @@ -710,18 +702,19 @@ private void RandomizeEnemies(TR1CombinedLevel level, EnemyRandomizationCollecti FixColosseumBats(level); } - if (level.Is(TR1LevelNames.TIHOCAN) && level.Data.Entities[82].TypeID != TR1Type.Pierre) + if (level.Is(TR1LevelNames.TIHOCAN) && (!Settings.RandomizeItems || !Settings.IncludeKeyItems)) { - // Add a guaranteed key at the end of the level. Item rando can reposition it. - level.Data.Entities.Add(new() + if (TR1EnemyUtilities.CanDropItems(level.Data.Entities[TR1ItemRandomizer.TihocanPierreIndex], level, floorData)) { - TypeID = TR1Type.Key1_S_P, - X = 30208, - Y = 2560, - Z = 91648, - Room = 110, - Intensity = 6144 - }); + // Whichever enemy has taken Pierre's place will drop the items. + level.Script.AddItemDrops(TR1ItemRandomizer.TihocanPierreIndex, TR1ItemRandomizer.TihocanEndItems + .Select(e => ItemUtilities.ConvertToScriptItem(e.TypeID))); + } + else + { + // Add physical pickups - this means item rando is off, so these won't move. + level.Data.Entities.AddRange(TR1ItemRandomizer.TihocanEndItems); + } } // Fix missing OG animation SFX @@ -733,7 +726,7 @@ private void RandomizeEnemies(TR1CombinedLevel level, EnemyRandomizationCollecti } // Add extra ammo based on this level's difficulty - if (Settings.CrossLevelEnemies && ScriptEditor.Edition.IsCommunityPatch && level.Script.RemovesWeapons) + if (Settings.CrossLevelEnemies && level.Script.RemovesWeapons) { AddUnarmedLevelAmmo(level); } @@ -757,7 +750,7 @@ private static int GetEntityCount(TR1CombinedLevel level, TR1Type entityType) } else if (type == TR1Type.AdamEgg || type == TR1Type.AtlanteanEgg) { - TR1Type eggType = CodeBitsToAtlantean(entity.CodeBits); + TR1Type eggType = TR1EnemyUtilities.CodeBitsToAtlantean(entity.CodeBits); if (eggType == translatedType && Array.Find(level.Data.Models, m => m.ID == (uint)eggType) != null) { count++; @@ -767,30 +760,6 @@ private static int GetEntityCount(TR1CombinedLevel level, TR1Type entityType) return count; } - private static ushort AtlanteanToCodeBits(TR1Type atlantean) - { - return atlantean switch - { - TR1Type.ShootingAtlantean_N => 1, - TR1Type.Centaur => 2, - TR1Type.Adam => 4, - TR1Type.NonShootingAtlantean_N => 8, - _ => 0, - }; - } - - private static TR1Type CodeBitsToAtlantean(ushort codeBits) - { - return codeBits switch - { - 1 => TR1Type.ShootingAtlantean_N, - 2 => TR1Type.Centaur, - 4 => TR1Type.Adam, - 8 => TR1Type.NonShootingAtlantean_N, - _ => TR1Type.FlyingAtlantean, - }; - } - private static bool IsEnemyInOrAboveWater(TR1Entity entity, TR1Level level, FDControl floorData) { if (level.Rooms[entity.Room].ContainsWater) @@ -1009,7 +978,7 @@ private void AddUnarmedLevelAmmo(TR1CombinedLevel level) { TR1Entity resultantEnemy = new() { - TypeID = CodeBitsToAtlantean(entity.CodeBits) + TypeID = TR1EnemyUtilities.CodeBitsToAtlantean(entity.CodeBits) }; // Only include it if the model is present i.e. it's not an empty egg. @@ -1270,7 +1239,7 @@ private void CloneEnemies(TR1CombinedLevel level) // entities inside the egg that will have already been accounted for. TR1Entity adamEgg = level.Data.Entities.Find(e => e.TypeID == TR1Type.AdamEgg); if (adamEgg != null - && CodeBitsToAtlantean(adamEgg.CodeBits) == TR1Type.Adam + && TR1EnemyUtilities.CodeBitsToAtlantean(adamEgg.CodeBits) == TR1Type.Adam && Array.Find(level.Data.Models, m => m.ID == (uint)TR1Type.Adam) != null) { enemies.Add(adamEgg); @@ -1360,7 +1329,7 @@ protected override void ProcessImpl() importModels.Add(TR1Type.Missile3_H); } - TR1ModelImporter importer = new(_outer.ScriptEditor.Edition.IsCommunityPatch) + TR1ModelImporter importer = new(true) { EntitiesToImport = importModels, EntitiesToRemove = enemies.EntitiesToRemove, diff --git a/TRRandomizerCore/Randomizers/TR1/TR1EnvironmentRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1EnvironmentRandomizer.cs index 544f0065b..8299a64a3 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1EnvironmentRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1EnvironmentRandomizer.cs @@ -106,7 +106,7 @@ private void RandomizeEnvironment(TR1CombinedLevel level) EMEditorMapping mapping = EMEditorMapping.Get(GetResourcePath(@"TR1\Environment\" + level.Name + "-Environment.json")); if (mapping != null) { - mapping.SetCommunityPatch(ScriptEditor.Edition.IsCommunityPatch); + mapping.SetCommunityPatch(true); ApplyMappingToLevel(level, mapping); } @@ -128,7 +128,7 @@ private void ApplyMappingToLevel(TR1CombinedLevel level, EMEditorMapping mapping { Generator = _generator }; - picker.LoadTags(Settings, ScriptEditor.Edition.IsCommunityPatch); + picker.LoadTags(Settings, true); picker.Options.ExclusionMode = EMExclusionMode.Individual; // These are applied whether or not environment randomization is enabled, @@ -188,13 +188,8 @@ private void ApplyMappingToLevel(TR1CombinedLevel level, EMEditorMapping mapping } } - private void UpdateDoppelgangerScript(TR1CombinedLevel level) + private static void UpdateDoppelgangerScript(TR1CombinedLevel level) { - if (!ScriptEditor.Edition.IsCommunityPatch) - { - return; - } - // Bacon Lara may have been added as a trap/puzzle, so we need to ensure the script knows // where to set her up. The mods will have stored this in a temporary flag as the entity // starting room may not necessarily be where her positioning should be calculated from. @@ -236,11 +231,11 @@ private void FinalizeEnvironment(TR1CombinedLevel level) EMEditorMapping mapping = EMEditorMapping.Get(GetResourcePath($@"TR1\Environment\{level.Name}-Environment.json")); EnvironmentPicker picker = new(Settings.HardEnvironmentMode); picker.Options.ExclusionMode = EMExclusionMode.Individual; - picker.ResetTags(ScriptEditor.Edition.IsCommunityPatch); + picker.ResetTags(true); if (mapping != null) { - mapping.SetCommunityPatch(ScriptEditor.Edition.IsCommunityPatch); + mapping.SetCommunityPatch(true); // Similar to All, but these mods will have conditions configured so may // or may not apply. Process these last so that conditions based on other @@ -263,11 +258,8 @@ private void FinalizeEnvironment(TR1CombinedLevel level) TextureMonitor monitor = TextureMonitor.CreateMonitor(level.Name); monitor.UseMirroring = true; - if (ScriptEditor.Edition.IsCommunityPatch) - { - // Remove the demo if it's set as it can crash the game - level.Script.Demo = null; - } + // Remove the demo if it's set as it can crash the game + level.Script.Demo = null; } } } diff --git a/TRRandomizerCore/Randomizers/TR1/TR1ItemRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1ItemRandomizer.cs index 3a28d7b27..ed320f6ff 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1ItemRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1ItemRandomizer.cs @@ -14,6 +14,30 @@ namespace TRRandomizerCore.Randomizers; public class TR1ItemRandomizer : BaseTR1Randomizer { + public const int TihocanPierreIndex = 82; + + public static readonly List TihocanEndItems = new() + { + new() + { + TypeID = TR1Type.Key1_S_P, + X = 30208, + Y = 2560, + Z = 91648, + Room = 110, + Intensity = 6144 + }, + new () + { + TypeID = TR1Type.ScionPiece2_S_P, + X = 34304, + Y = 2560, + Z = 91648, + Room = 110, + Intensity = 6144 + } + }; + // The number of extra pickups to add per level private static readonly Dictionary _extraItemCounts = new() { @@ -115,9 +139,11 @@ public override void Randomize(int seed) } } - if (ScriptEditor.Edition.IsCommunityPatch && Settings.UseRecommendedCommunitySettings) + if (Settings.UseRecommendedCommunitySettings) { - (ScriptEditor.Script as TR1Script).Enable3dPickups = false; + TR1Script script = ScriptEditor.Script as TR1Script; + script.Enable3dPickups = false; + script.ConvertDroppedGuns = true; ScriptEditor.SaveScript(); } } @@ -199,6 +225,21 @@ public void RandomizeItemTypes(TR1CombinedLevel level) List stdItemTypes = TR1TypeUtilities.GetStandardPickupTypes(); stdItemTypes.Remove(TR1Type.PistolAmmo_S_P); // Sprite/model not available + // Randomize scripted item drops if we have default enemies. + foreach (TR1Entity enemy in level.Data.Entities.Where(e => TR1TypeUtilities.IsEnemyType(e.TypeID))) + { + int enemyIndex = level.Data.Entities.IndexOf(enemy); + TR1ItemDrop drop = level.Script.ItemDrops.Find(d => d.EnemyNum == enemyIndex); + for (int i = 0; i < drop?.ObjectIds.Count; i++) + { + TR1Type dropType = ItemUtilities.ConvertToEntity(drop.ObjectIds[i]); + if (stdItemTypes.Contains(dropType)) + { + drop.ObjectIds[i] = ItemUtilities.ConvertToScriptItem(stdItemTypes[_generator.Next(0, stdItemTypes.Count)]); + } + } + } + bool hasPistols = level.Data.Entities.Any(e => e.TypeID == TR1Type.Pistols_S_P); for (int i = 0; i < level.Data.Entities.Count; i++) @@ -285,6 +326,14 @@ public void RandomizeItemLocations(TR1CombinedLevel level) return; } + FDControl floorData = new(); + floorData.ParseFromLevel(level.Data); + + // TR1X allows us to keep the end-level stats accurate. All generated locations + // should be reachable, but this may be modifed in TestEnemyItemDrop where items + // are hidden. + level.Script.UnobtainablePickups = null; + for (int i = 0; i < level.Data.Entities.Count; i++) { if (_secretMapping.RewardEntities.Contains(i) @@ -299,15 +348,9 @@ public void RandomizeItemLocations(TR1CombinedLevel level) { _picker.RandomizePickupLocation(entity); entity.Intensity = 0; + TestEnemyItemDrop(level, entity, floorData); } } - - if (ScriptEditor.Edition.IsCommunityPatch) - { - // T1M allows us to keep the end-level stats accurate. All generated locations - // should be reachable. - level.Script.UnobtainablePickups = null; - } } private List GetItemLocationPool(TR1CombinedLevel level, bool keyItemMode) @@ -356,25 +399,83 @@ private void RandomizeKeyItems(TR1CombinedLevel level) _picker.Initialise(_levelInstance.Name, GetItemLocationPool(_levelInstance, true), Settings, _generator); + if (level.Is(TR1LevelNames.TIHOCAN)) + { + // Enemy rando may not be selected or Pierre may have ended up at the + // end as usual. Remove his key and scion drops and place them as items. + if (level.Data.Entities[TihocanPierreIndex].TypeID == TR1Type.Pierre) + { + level.Script.ItemDrops.Find(d => d.EnemyNum == TihocanPierreIndex)?.ObjectIds + .RemoveAll(e => TihocanEndItems.Select(i => ItemUtilities.ConvertToScriptItem(i.TypeID)).Contains(e)); + } + level.Data.Entities.AddRange(TihocanEndItems); + } + for (int i = 0; i < level.Data.Entities.Count; i++) { TR1Entity entity = level.Data.Entities[i]; - if (!TR1TypeUtilities.IsKeyItemType(entity.TypeID) + if (!IsMovableKeyItem(level, entity) || ItemFactory.IsItemLocked(level.Name, i)) { continue; } - _picker.RandomizeKeyItemLocation( - entity, LocationUtilities.HasPickupTriger(entity, i, level.Data, floorData), + bool hasPickupTrigger = LocationUtilities.HasPickupTriger(entity, i, level.Data, floorData); + _picker.RandomizeKeyItemLocation(entity, hasPickupTrigger, level.Script.OriginalSequence, level.Data.Rooms[entity.Room].Info); + + if (Settings.AllowEnemyKeyDrops && !hasPickupTrigger) + { + TestEnemyItemDrop(level, entity, floorData); + } + } + } + + private void TestEnemyItemDrop(TR1CombinedLevel level, TR1Entity entity, FDControl floorData) + { + TRRoomSector sectorFunc(Location loc) => + FDUtilities.GetRoomSector(loc.X, loc.Y, loc.Z, (short)loc.Room, level.Data, floorData); + + // There may be several enemies in one spot e.g. in cloned enemy mode. Pick one + // at random for each call of this method. Always exclude empty eggs. + + Location floor = entity.GetFloorLocation(sectorFunc); + List enemies = level.Data.Entities + .FindAll(e => TR1TypeUtilities.IsEnemyType(e.TypeID) || e.TypeID == TR1Type.AdamEgg) + .FindAll(e => e.GetFloorLocation(sectorFunc).IsEquivalent(floor)); + + if (enemies.Count == 0 || enemies.All(e => !TR1EnemyUtilities.CanDropItems(e, level, floorData))) + { + return; + } + + TR1Entity enemy; + do + { + enemy = enemies[_generator.Next(0, enemies.Count)]; + } + while (!TR1EnemyUtilities.CanDropItems(enemy, level, floorData)); + + level.Script.AddItemDrops(level.Data.Entities.IndexOf(enemy), ItemUtilities.ConvertToScriptItem(entity.TypeID)); + ItemUtilities.HideEntity(entity); + + // Retain the type for quick lookup in trview, but mark it as OOB for the stats. + if (!level.Script.UnobtainablePickups.HasValue) + { + level.Script.UnobtainablePickups = 0; } + level.Script.UnobtainablePickups++; + } + + private static bool IsMovableKeyItem(TR1CombinedLevel level, TR1Entity entity) + { + return TR1TypeUtilities.IsKeyItemType(entity.TypeID) + || (level.Is(TR1LevelNames.TIHOCAN) && entity.TypeID == TR1Type.ScionPiece2_S_P); } private void RandomizeSprites() { - if (ScriptEditor.Edition.IsCommunityPatch - && !Settings.UseRecommendedCommunitySettings + if (!Settings.UseRecommendedCommunitySettings && (ScriptEditor.Script as TR1Script).Enable3dPickups) { // With 3D pickups enabled, sprite randomization is meaningless diff --git a/TRRandomizerCore/Randomizers/TR1/TR1OutfitRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1OutfitRandomizer.cs index 966e29b15..0febb872f 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1OutfitRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1OutfitRandomizer.cs @@ -156,7 +156,7 @@ private void ChooseFilteredLevels() } } - if (ScriptEditor.Edition.IsCommunityPatch && _braidLevels.Count > 0) + if (_braidLevels.Count > 0) { (ScriptEditor.Script as TR1Script).EnableBraid = true; } @@ -164,7 +164,7 @@ private void ChooseFilteredLevels() private bool IsBraidLevel(TR1ScriptedLevel lvl) { - return ScriptEditor.Edition.IsCommunityPatch && _braidLevels.Contains(lvl); + return _braidLevels.Contains(lvl); } private bool IsInvisibleLevel(TR1ScriptedLevel lvl) @@ -289,7 +289,7 @@ protected override void ProcessImpl() private void ImportBraid(TR1CombinedLevel level) { - TR1ModelImporter importer = new(_outer.ScriptEditor.Edition.IsCommunityPatch) + TR1ModelImporter importer = new(true) { Level = level.Data, LevelName = level.Name, @@ -381,7 +381,7 @@ private static void CreateGoldenBraid(TR1CombinedLevel level) ponytail.NumMeshes *= 2; } - private void HideEntities(TR1CombinedLevel level, IEnumerable entities) + private static void HideEntities(TR1CombinedLevel level, IEnumerable entities) { MeshEditor editor = new(); foreach (TR1Type ent in entities) @@ -459,8 +459,8 @@ private bool CutsceneSupportsBraid(TR1CombinedLevel parentLevel) } // Not supported before 2.13, so don't make any changes to Lara here. - Version t1mVersion = _outer.ScriptEditor.Edition.ExeVersion; - if (t1mVersion == null || t1mVersion < _minBraidCutsceneVersion) + Version tr1xVersion = _outer.ScriptEditor.Edition.ExeVersion; + if (tr1xVersion == null || tr1xVersion < _minBraidCutsceneVersion) { return false; } @@ -587,7 +587,7 @@ private void ConvertToGymOutfit(TR1CombinedLevel level) if (level.HasCutScene) { - TR1ModelImporter importer = new(_outer.ScriptEditor.Edition.IsCommunityPatch) + TR1ModelImporter importer = new(true) { Level = level.CutSceneLevel.Data, LevelName = level.CutSceneLevel.Name, @@ -667,7 +667,7 @@ private void ConvertToPartialGymOutfit(TR1CombinedLevel level) if (level.HasCutScene) { - TR1ModelImporter importer = new(_outer.ScriptEditor.Edition.IsCommunityPatch) + TR1ModelImporter importer = new(true) { Level = level.CutSceneLevel.Data, LevelName = level.CutSceneLevel.Name, @@ -851,7 +851,7 @@ private void ConvertToMauledOutfit(TR1CombinedLevel level) if (level.HasCutScene && !level.Is(TR1LevelNames.MINES)) { - TR1ModelImporter importer = new(_outer.ScriptEditor.Edition.IsCommunityPatch) + TR1ModelImporter importer = new(true) { Level = level.CutSceneLevel.Data, LevelName = level.CutSceneLevel.Name, diff --git a/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs index 965260152..5278b70e8 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1SecretRandomizer.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; using System.Diagnostics; -using TREnvironmentEditor.Helpers; using TREnvironmentEditor.Model.Types; using TRFDControl; using TRFDControl.FDEntryTypes; @@ -58,7 +57,7 @@ public override void Randomize(int seed) RouteManager = _routePicker, }; - if (ScriptEditor.Edition.IsCommunityPatch && !Settings.UseSecretPack) + if (!Settings.UseSecretPack) { SetSecretCounts(); } @@ -110,31 +109,31 @@ public override void Randomize(int seed) _processingException?.Throw(); - if (ScriptEditor.Edition.IsCommunityPatch) - { - TR1Script script = ScriptEditor.Script as TR1Script; - script.FixPyramidSecretTrigger = false; + TR1Script script = ScriptEditor.Script as TR1Script; + script.FixPyramidSecretTrigger = false; - if (Settings.UseRecommendedCommunitySettings) - { - script.Enable3dPickups = false; - } - - if (Settings.GlitchedSecrets) + if (Settings.UseRecommendedCommunitySettings) + { + script.Enable3dPickups = false; + if (Settings.HardSecrets || Settings.GlitchedSecrets) { - script.FixDescendingGlitch = false; - script.FixQwopGlitch = false; - script.FixWallJumpGlitch = false; + script.EnableBuffering = true; } + } - ScriptEditor.SaveScript(); + if (Settings.GlitchedSecrets) + { + script.FixDescendingGlitch = false; + script.FixQwopGlitch = false; + script.FixWallJumpGlitch = false; } + + ScriptEditor.SaveScript(); } private bool Are3DPickupsEnabled() { - return ScriptEditor.Edition.IsCommunityPatch - && !Settings.UseRecommendedCommunitySettings + return !Settings.UseRecommendedCommunitySettings && (ScriptEditor.Script as TR1Script).Enable3dPickups; } @@ -356,9 +355,9 @@ private static void CreateTrapdoorTrigger(TR1Entity door, ushort doorIndex, TR1L { new EMTriggerFunction { - Locations = new List + Locations = new() { - new EMLocation + new() { X = door.X, Y = door.Y, @@ -366,12 +365,12 @@ private static void CreateTrapdoorTrigger(TR1Entity door, ushort doorIndex, TR1L Room = door.Room } }, - Trigger = new EMTrigger + Trigger = new() { TrigType = FDTrigType.Dummy, - Actions = new List + Actions = new() { - new EMTriggerAction + new() { Parameter = (short)doorIndex } @@ -493,17 +492,17 @@ private void AddDamageControl(TR1CombinedLevel level, List locations) TR1Entity medi = ItemFactory.CreateItem(level.Name, level.Data.Entities, location, Settings.DevelopmentMode); medi.TypeID = TR1Type.LargeMed_S_P; } - else if (ScriptEditor.Edition.IsCommunityPatch) + else { - level.Script.AddStartInventoryItem(TR1Items.LargeMedi); + level.Script.AddStartInventoryItem(TR1Items.LargeMed_S_P); } } // If we have also used a secret that requires damage and is glitched, add an additional // medi as these tend to occur where Lara has to drop far after picking them up. - if (locations.Any(l => l.RequiresDamage && l.RequiresGlitch) && ScriptEditor.Edition.IsCommunityPatch) + if (locations.Any(l => l.RequiresDamage && l.RequiresGlitch)) { - level.Script.AddStartInventoryItem(TR1Items.SmallMedi); + level.Script.AddStartInventoryItem(TR1Items.SmallMed_S_P); } } @@ -634,8 +633,7 @@ private void PlaceSecret(TR1CombinedLevel level, TRSecretPlacement secr } // Turn off walk-to-items in TR1X if we are placing on a slope above water. - if (ScriptEditor.Edition.IsCommunityPatch - && !level.Data.Rooms[secret.Location.Room].ContainsWater + if (!level.Data.Rooms[secret.Location.Room].ContainsWater && secret.Location.IsSlipperySlope(level.Data, floorData)) { (ScriptEditor as TR1ScriptEditor).WalkToItems = false; @@ -864,7 +862,7 @@ protected override void ProcessImpl() TRSecretModelAllocation allocation = _importAllocations[level]; // Get the artefacts into the level and refresh the model list - TR1ModelImporter importer = new(_outer.ScriptEditor.Edition.IsCommunityPatch) + TR1ModelImporter importer = new(true) { Level = level.Data, LevelName = level.Name, @@ -890,7 +888,7 @@ protected override void ProcessImpl() if (secretModelType == TR1Type.SecretScion_M_H && _outer.Are3DPickupsEnabled()) { - // T1M embeds scions into the ground when they are puzzle/key types in 3D mode, + // TR1X embeds scions into the ground when they are puzzle/key types in 3D mode, // so we counteract that here to avoid uncollectable items. TRMesh scionMesh = TRMeshUtilities.GetModelFirstMesh(level.Data, puzzleModelType); foreach (TRVertex vertex in scionMesh.Vertices) diff --git a/TRRandomizerCore/Randomizers/TR1/TR1TextureRandomizer.cs b/TRRandomizerCore/Randomizers/TR1/TR1TextureRandomizer.cs index b9021b448..78e4bce5f 100644 --- a/TRRandomizerCore/Randomizers/TR1/TR1TextureRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR1/TR1TextureRandomizer.cs @@ -171,7 +171,7 @@ private void ChooseWireframeLevels() _persistentWireColour = _wireframeColours[_generator.Next(0, _wireframeColours.Length)]; } - bool has3DPickups = ScriptEditor.Edition.IsCommunityPatch && (ScriptEditor as TR1ScriptEditor).Enable3dPickups; + bool has3DPickups = (ScriptEditor as TR1ScriptEditor).Enable3dPickups; foreach (WireframeData data in _wireframeData.Values.ToList()) { data.Has3DPickups = has3DPickups; @@ -269,7 +269,7 @@ private Color GetWireframeVariant(bool overridePersistent = false) private void RandomizeWater(TR1CombinedLevel level) { - if (!Settings.RandomizeWaterColour || !ScriptEditor.Edition.IsCommunityPatch) + if (!Settings.RandomizeWaterColour) { return; } @@ -306,12 +306,12 @@ internal class TextureProcessor : AbstractProcessorThread internal TextureProcessor(TR1TextureRandomizer outer) : base(outer) { - _holders = new Dictionary>(); - _landmarkImporter = new TR1LandmarkImporter + _holders = new(); + _landmarkImporter = new() { - IsCommunityPatch = _outer.ScriptEditor.Edition.IsCommunityPatch + IsCommunityPatch = true }; - _wireframer = new TR1Wireframer(); + _wireframer = new(); } internal void AddLevel(TR1CombinedLevel level) @@ -341,7 +341,7 @@ protected override void StartImpl() DynamicTextureBuilder dynamicBuilder = new() { RetainMainTextures = _outer.Settings.RetainMainLevelTextures, - IsCommunityPatch = _outer.ScriptEditor.Edition.IsCommunityPatch + IsCommunityPatch = true }; foreach (TR1CombinedLevel level in levels) { diff --git a/TRRandomizerCore/Randomizers/TR2/TR2ItemRandomizer.cs b/TRRandomizerCore/Randomizers/TR2/TR2ItemRandomizer.cs index 4bb644afb..cbba0da64 100644 --- a/TRRandomizerCore/Randomizers/TR2/TR2ItemRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR2/TR2ItemRandomizer.cs @@ -222,7 +222,7 @@ private void RandomizeKeyItems(TR2CombinedLevel level) floorData.ParseFromLevel(level.Data); _picker.TriggerTestAction = location => LocationUtilities.HasAnyTrigger(location, level.Data, floorData); - _picker.KeyItemTestAction = location => TestKeyItemLocation(location, level); + _picker.KeyItemTestAction = (location, hasPickupTrigger) => TestKeyItemLocation(location, hasPickupTrigger, level); _picker.RoomInfos = level.Data.Rooms .Select(r => new ExtRoomInfo(r.Info, r.NumXSectors, r.NumZSectors)) .ToList(); @@ -299,7 +299,7 @@ private void AdjustSeraphContinuity(TR2CombinedLevel level) } } - private bool TestKeyItemLocation(Location location, TR2CombinedLevel level) + private bool TestKeyItemLocation(Location location, bool hasPickupTrigger, TR2CombinedLevel level) { // Make sure if we're placing on the same tile as an enemy, that the // enemy can drop the item. @@ -307,7 +307,7 @@ private bool TestKeyItemLocation(Location location, TR2CombinedLevel level) .FindAll(e => TR2TypeUtilities.IsEnemyType(e.TypeID)) .Find(e => e.GetLocation().IsEquivalent(location)); - return enemy == null || (Settings.AllowEnemyKeyDrops && TR2TypeUtilities.CanDropPickups + return enemy == null || (Settings.AllowEnemyKeyDrops && !hasPickupTrigger && TR2TypeUtilities.CanDropPickups ( TR2TypeUtilities.GetAliasForLevel(level.Name, enemy.TypeID), Settings.RandomizeEnemies && !Settings.ProtectMonks, diff --git a/TRRandomizerCore/Randomizers/TR3/TR3ItemRandomizer.cs b/TRRandomizerCore/Randomizers/TR3/TR3ItemRandomizer.cs index 807b91276..bb97fbb90 100644 --- a/TRRandomizerCore/Randomizers/TR3/TR3ItemRandomizer.cs +++ b/TRRandomizerCore/Randomizers/TR3/TR3ItemRandomizer.cs @@ -334,7 +334,7 @@ public void RandomizeKeyItems(TR3CombinedLevel level) floorData.ParseFromLevel(level.Data); _picker.TriggerTestAction = location => LocationUtilities.HasAnyTrigger(location, level.Data, floorData); - _picker.KeyItemTestAction = location => TestKeyItemLocation(location, level); + _picker.KeyItemTestAction = (location, hasPickupTrigger) => TestKeyItemLocation(location, hasPickupTrigger, level); _picker.RoomInfos = level.Data.Rooms .Select(r => new ExtRoomInfo(r.Info, r.NumXSectors, r.NumZSectors)) .ToList(); @@ -356,7 +356,7 @@ public void RandomizeKeyItems(TR3CombinedLevel level) } } - private bool TestKeyItemLocation(Location location, TR3CombinedLevel level) + private bool TestKeyItemLocation(Location location, bool hasPickupTrigger, TR3CombinedLevel level) { // Make sure if we're placing on the same tile as an enemy, that the // enemy can drop the item. @@ -364,7 +364,7 @@ private bool TestKeyItemLocation(Location location, TR3CombinedLevel level) .FindAll(e => TR3TypeUtilities.IsEnemyType(e.TypeID)) .Find(e => e.GetLocation().IsEquivalent(location)); - return enemy == null || (Settings.AllowEnemyKeyDrops && TR3TypeUtilities.CanDropPickups + return enemy == null || (Settings.AllowEnemyKeyDrops && !hasPickupTrigger && TR3TypeUtilities.CanDropPickups ( TR3TypeUtilities.GetAliasForLevel(level.Name, enemy.TypeID), !Settings.RandomizeEnemies || Settings.ProtectMonks diff --git a/TRRandomizerCore/Resources/Shared/Graphics/goldbadge-large.png b/TRRandomizerCore/Resources/Shared/Graphics/goldbadge-large.png index 64f101aeb..46eaa053a 100644 Binary files a/TRRandomizerCore/Resources/Shared/Graphics/goldbadge-large.png and b/TRRandomizerCore/Resources/Shared/Graphics/goldbadge-large.png differ diff --git a/TRRandomizerCore/Resources/TR1/Environment/LEVEL1.PHD-Environment.json b/TRRandomizerCore/Resources/TR1/Environment/LEVEL1.PHD-Environment.json index add18688d..2ffe4ae00 100644 --- a/TRRandomizerCore/Resources/TR1/Environment/LEVEL1.PHD-Environment.json +++ b/TRRandomizerCore/Resources/TR1/Environment/LEVEL1.PHD-Environment.json @@ -2440,7 +2440,7 @@ ], [ { - "Comments": "Make a Bacon Lara puzzle at the end (T1M only).", + "Comments": "Make a Bacon Lara puzzle at the end.", "EMType": 126, "Tags": [ 5, diff --git a/TRRandomizerCore/Resources/TR1/Locations/routes.json b/TRRandomizerCore/Resources/TR1/Locations/routes.json index 795548ddc..229aa159e 100644 --- a/TRRandomizerCore/Resources/TR1/Locations/routes.json +++ b/TRRandomizerCore/Resources/TR1/Locations/routes.json @@ -4014,7 +4014,7 @@ "Y": 1024, "Z": 1536, "Room": 20, - "KeyItemsLow": "18133,18277,18267,18389", + "KeyItemsLow": "18133,18277,18267,18389,18444", "Range": "Large", "RequiresReturnPath": true }, @@ -4434,7 +4434,7 @@ "Z": 68096, "Room": 77, "KeyItemsHigh": "18133", - "KeyItemsLow": "18389", + "KeyItemsLow": "18389,18444", "Range": "Medium", "RequiresReturnPath": true }, @@ -4552,7 +4552,7 @@ "Y": 6656, "Z": 67840, "Room": 99, - "KeyItemsLow": "18389", + "KeyItemsLow": "18389,18444", "Range": "Medium" }, { @@ -4560,7 +4560,7 @@ "Y": 6656, "Z": 68096, "Room": 99, - "KeyItemsLow": "18389", + "KeyItemsLow": "18389,18444", "Range": "Large" }, { @@ -4640,14 +4640,14 @@ "Y": 7168, "Z": 78336, "Room": 98, - "KeyItemsLow": "18389" + "KeyItemsLow": "18389,18444" }, { "X": 28160, "Y": 7168, "Z": 78336, "Room": 98, - "KeyItemsLow": "18389", + "KeyItemsLow": "18389,18444", "RequiresReturnPath": true }, { @@ -4712,14 +4712,14 @@ "Y": 3840, "Z": 94720, "Room": 112, - "KeyItemsHigh": "18389" + "KeyItemsHigh": "18389,18444" }, { "X": 32256, "Y": 3840, "Z": 94720, "Room": 112, - "KeyItemsHigh": "18389", + "KeyItemsHigh": "18389,18444", "RequiresReturnPath": true }, { @@ -4727,7 +4727,7 @@ "Y": 3840, "Z": 94720, "Room": 112, - "KeyItemsHigh": "18389", + "KeyItemsHigh": "18389,18444", "Range": "Medium" }, { @@ -4735,7 +4735,7 @@ "Y": 3840, "Z": 94976, "Room": 112, - "KeyItemsHigh": "18389", + "KeyItemsHigh": "18389,18444", "Range": "Medium", "RequiresReturnPath": true }, @@ -4744,7 +4744,7 @@ "Y": 3840, "Z": 94976, "Room": 112, - "KeyItemsHigh": "18389", + "KeyItemsHigh": "18389,18444", "Range": "Large" }, { @@ -4752,7 +4752,7 @@ "Y": 3840, "Z": 94976, "Room": 112, - "KeyItemsHigh": "18389", + "KeyItemsHigh": "18389,18444", "Range": "Large", "RequiresReturnPath": true } diff --git a/TRRandomizerCore/TRRandomizerController.cs b/TRRandomizerCore/TRRandomizerController.cs index d45d8ccbf..35d4ec09a 100644 --- a/TRRandomizerCore/TRRandomizerController.cs +++ b/TRRandomizerCore/TRRandomizerController.cs @@ -381,75 +381,45 @@ public bool AutoLaunchGame } #endregion - #region T1M Specifics + #region TR1X Specifics public bool EnableGameModes { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableGameModes; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableGameModes = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableGameModes; + set => (ScriptEditor as TR1ScriptEditor).EnableGameModes = value; } public bool EnableSaveCrystals { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableSaveCrystals; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableSaveCrystals = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableSaveCrystals; + set => (ScriptEditor as TR1ScriptEditor).EnableSaveCrystals = value; } public double DemoDelay { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.DemoTime : -1; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.DemoTime = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).DemoTime; + set => (ScriptEditor as TR1ScriptEditor).DemoTime = value; } public double DrawDistanceFade { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.DrawDistanceFade : -1; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.DrawDistanceFade = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).DrawDistanceFade; + set => (ScriptEditor as TR1ScriptEditor).DrawDistanceFade = value; } public double DrawDistanceMax { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.DrawDistanceMax : -1; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.DrawDistanceMax = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).DrawDistanceMax; + set => (ScriptEditor as TR1ScriptEditor).DrawDistanceMax = value; } public double[] WaterColor { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.WaterColor : null; + get => (ScriptEditor as TR1ScriptEditor).WaterColor; set { - if (ScriptEditor is TR1ScriptEditor tr1Editor && value.Length == 3) + if (value.Length == 3) { - tr1Editor.WaterColor = new double[] + (ScriptEditor as TR1ScriptEditor).WaterColor = new double[] { Math.Round(value[0], 2), Math.Round(value[1], 2), @@ -461,746 +431,410 @@ public double[] WaterColor public bool DisableMagnums { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.DisableMagnums; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.DisableMagnums = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).DisableMagnums; + set => (ScriptEditor as TR1ScriptEditor).DisableMagnums = value; } public bool DisableUzis { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.DisableUzis; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.DisableUzis = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).DisableUzis; + set => (ScriptEditor as TR1ScriptEditor).DisableUzis = value; } public bool DisableShotgun { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.DisableShotgun; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.DisableShotgun = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).DisableShotgun; + set => (ScriptEditor as TR1ScriptEditor).DisableShotgun = value; } public bool EnableDeathsCounter { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableDeathsCounter; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableDeathsCounter = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableDeathsCounter; + set => (ScriptEditor as TR1ScriptEditor).EnableDeathsCounter = value; } public bool EnableEnemyHealthbar { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableEnemyHealthbar; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableEnemyHealthbar = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableEnemyHealthbar; + set => (ScriptEditor as TR1ScriptEditor).EnableEnemyHealthbar = value; } public bool EnableEnhancedLook { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableEnhancedLook; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableEnhancedLook = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableEnhancedLook; + set => (ScriptEditor as TR1ScriptEditor).EnableEnhancedLook = value; } public bool EnableShotgunFlash { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableShotgunFlash; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableShotgunFlash = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableShotgunFlash; + set => (ScriptEditor as TR1ScriptEditor).EnableShotgunFlash = value; } public bool FixShotgunTargeting { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixShotgunTargeting; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixShotgunTargeting = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixShotgunTargeting; + set => (ScriptEditor as TR1ScriptEditor).FixShotgunTargeting = value; } public bool EnableNumericKeys { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableNumericKeys; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableNumericKeys = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableNumericKeys; + set => (ScriptEditor as TR1ScriptEditor).EnableNumericKeys = value; } public bool EnableTr3Sidesteps { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableTr3Sidesteps; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableTr3Sidesteps = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableTr3Sidesteps; + set => (ScriptEditor as TR1ScriptEditor).EnableTr3Sidesteps = value; } public bool EnableCheats { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableCheats; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableCheats = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableCheats; + set => (ScriptEditor as TR1ScriptEditor).EnableCheats = value; } public bool EnableDetailedStats { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableDetailedStats; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableDetailedStats = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableDetailedStats; + set => (ScriptEditor as TR1ScriptEditor).EnableDetailedStats = value; } public bool EnableCompassStats { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableCompassStats; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableCompassStats = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableCompassStats; + set => (ScriptEditor as TR1ScriptEditor).EnableCompassStats = value; } public bool EnableTotalStats { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableTotalStats; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableTotalStats = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableTotalStats; + set => (ScriptEditor as TR1ScriptEditor).EnableTotalStats = value; } public bool EnableTimerInInventory { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableTimerInInventory; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableTimerInInventory = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableTimerInInventory; + set => (ScriptEditor as TR1ScriptEditor).EnableTimerInInventory = value; } public bool EnableSmoothBars { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableSmoothBars; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableSmoothBars = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableSmoothBars; + set => (ScriptEditor as TR1ScriptEditor).EnableSmoothBars = value; } public bool EnableFadeEffects { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableFadeEffects; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableFadeEffects = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableFadeEffects; + set => (ScriptEditor as TR1ScriptEditor).EnableFadeEffects = value; } public TRMenuStyle MenuStyle { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.MenuStyle : TRMenuStyle.PC; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.MenuStyle = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).MenuStyle; + set => (ScriptEditor as TR1ScriptEditor).MenuStyle = value; } public TRHealthbarMode HealthbarShowingMode { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.HealthbarShowingMode : TRHealthbarMode.FlashingOrDefault; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.HealthbarShowingMode = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).HealthbarShowingMode; + set => (ScriptEditor as TR1ScriptEditor).HealthbarShowingMode = value; } public TRUILocation HealthbarLocation { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.HealthbarLocation : TRUILocation.TopLeft; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.HealthbarLocation = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).HealthbarLocation; + set => (ScriptEditor as TR1ScriptEditor).HealthbarLocation = value; } public TRUIColour HealthbarColor { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.HealthbarColor : TRUIColour.Red; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.HealthbarColor = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).HealthbarColor; + set => (ScriptEditor as TR1ScriptEditor).HealthbarColor = value; } public TRAirbarMode AirbarShowingMode { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.AirbarShowingMode : TRAirbarMode.Default; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.AirbarShowingMode = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).AirbarShowingMode; + set => (ScriptEditor as TR1ScriptEditor).AirbarShowingMode = value; } public TRUILocation AirbarLocation { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.AirbarLocation : TRUILocation.TopRight; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.AirbarLocation = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).AirbarLocation; + set => (ScriptEditor as TR1ScriptEditor).AirbarLocation = value; } public TRUIColour AirbarColor { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.AirbarColor : TRUIColour.Blue; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.AirbarColor = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).AirbarColor; + set => (ScriptEditor as TR1ScriptEditor).AirbarColor = value; } public TRUILocation EnemyHealthbarLocation { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.EnemyHealthbarLocation : TRUILocation.BottomLeft; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnemyHealthbarLocation = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnemyHealthbarLocation; + set => (ScriptEditor as TR1ScriptEditor).EnemyHealthbarLocation = value; } public TRUIColour EnemyHealthbarColor { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.EnemyHealthbarColor : TRUIColour.Grey; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnemyHealthbarColor = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnemyHealthbarColor; + set => (ScriptEditor as TR1ScriptEditor).EnemyHealthbarColor = value; } public bool FixTihocanSecretSound { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixTihocanSecretSound; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixTihocanSecretSound = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixTihocanSecretSound; + set => (ScriptEditor as TR1ScriptEditor).FixTihocanSecretSound = value; } public bool FixPyramidSecretTrigger { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixPyramidSecretTrigger; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixPyramidSecretTrigger = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixPyramidSecretTrigger; + set => (ScriptEditor as TR1ScriptEditor).FixPyramidSecretTrigger = value; } public bool FixSecretsKillingMusic { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixSecretsKillingMusic; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixSecretsKillingMusic = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixSecretsKillingMusic; + set => (ScriptEditor as TR1ScriptEditor).FixSecretsKillingMusic = value; } public bool FixSpeechesKillingMusic { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixSpeechesKillingMusic; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixSpeechesKillingMusic = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixSpeechesKillingMusic; + set => (ScriptEditor as TR1ScriptEditor).FixSpeechesKillingMusic = value; } public bool FixDescendingGlitch { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixDescendingGlitch; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixDescendingGlitch = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixDescendingGlitch; + set => (ScriptEditor as TR1ScriptEditor).FixDescendingGlitch = value; } public bool FixWallJumpGlitch { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixWallJumpGlitch; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixWallJumpGlitch = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixWallJumpGlitch; + set => (ScriptEditor as TR1ScriptEditor).FixWallJumpGlitch = value; } public bool FixBridgeCollision { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixBridgeCollision; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixBridgeCollision = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixBridgeCollision; + set => (ScriptEditor as TR1ScriptEditor).FixBridgeCollision = value; } public bool FixQwopGlitch { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixQwopGlitch; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixQwopGlitch = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixQwopGlitch; + set => (ScriptEditor as TR1ScriptEditor).FixQwopGlitch = value; } public bool FixAlligatorAi { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixAlligatorAi; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixAlligatorAi = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixAlligatorAi; + set => (ScriptEditor as TR1ScriptEditor).FixAlligatorAi = value; } public bool ChangePierreSpawn { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.ChangePierreSpawn; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.ChangePierreSpawn = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).ChangePierreSpawn; + set => (ScriptEditor as TR1ScriptEditor).ChangePierreSpawn = value; } public int FovValue { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.FovValue : -1; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FovValue = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FovValue; + set => (ScriptEditor as TR1ScriptEditor).FovValue = value; } public bool FovVertical { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FovVertical; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FovVertical = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FovVertical; + set => (ScriptEditor as TR1ScriptEditor).FovVertical = value; } public bool EnableFmv { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableFmv; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableFmv = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableFmv; + set => (ScriptEditor as TR1ScriptEditor).EnableFmv = value; } public bool EnableCine { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableCine; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableCine = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableCine; + set => (ScriptEditor as TR1ScriptEditor).EnableCine = value; } public bool EnableMusicInMenu { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableMusicInMenu; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableMusicInMenu = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableMusicInMenu; + set => (ScriptEditor as TR1ScriptEditor).EnableMusicInMenu = value; } public bool EnableMusicInInventory { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableMusicInInventory; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableMusicInInventory = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableMusicInInventory; + set => (ScriptEditor as TR1ScriptEditor).EnableMusicInInventory = value; } public bool DisableTRexCollision { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.DisableTrexCollision; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.DisableTrexCollision = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).DisableTrexCollision; + set => (ScriptEditor as TR1ScriptEditor).DisableTrexCollision = value; } public double AnisotropyFilter { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.AnisotropyFilter : -1; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.AnisotropyFilter = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).AnisotropyFilter; + set => (ScriptEditor as TR1ScriptEditor).AnisotropyFilter = value; } public int ResolutionWidth { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.ResolutionWidth : -1; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.ResolutionWidth = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).ResolutionWidth; + set => (ScriptEditor as TR1ScriptEditor).ResolutionWidth = value; } public int ResolutionHeight { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.ResolutionHeight : -1; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.ResolutionHeight = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).ResolutionHeight; + set => (ScriptEditor as TR1ScriptEditor).ResolutionHeight = value; } public bool EnableRoundShadow { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableRoundShadow; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableRoundShadow = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableRoundShadow; + set => (ScriptEditor as TR1ScriptEditor).EnableRoundShadow = value; } public bool Enable3dPickups { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.Enable3dPickups; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.Enable3dPickups = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).Enable3dPickups; + set => (ScriptEditor as TR1ScriptEditor).Enable3dPickups = value; } public TRScreenshotFormat ScreenshotFormat { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.ScreenshotFormat : TRScreenshotFormat.JPG; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.ScreenshotFormat = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).ScreenshotFormat; + set => (ScriptEditor as TR1ScriptEditor).ScreenshotFormat = value; } public bool WalkToItems { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.WalkToItems; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.WalkToItems = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).WalkToItems; + set => (ScriptEditor as TR1ScriptEditor).WalkToItems = value; } public int MaximumSaveSlots { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.MaximumSaveSlots : 25; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.MaximumSaveSlots = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).MaximumSaveSlots; + set => (ScriptEditor as TR1ScriptEditor).MaximumSaveSlots = value; } public bool RevertToPistols { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.RevertToPistols; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.RevertToPistols = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).RevertToPistols; + set => (ScriptEditor as TR1ScriptEditor).RevertToPistols = value; } public bool EnableEnhancedSaves { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableEnhancedSaves; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableEnhancedSaves = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableEnhancedSaves; + set => (ScriptEditor as TR1ScriptEditor).EnableEnhancedSaves = value; } public bool EnablePitchedSounds { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnablePitchedSounds; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnablePitchedSounds = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnablePitchedSounds; + set => (ScriptEditor as TR1ScriptEditor).EnablePitchedSounds = value; } public bool EnableJumpTwists { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableJumpTwists; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableJumpTwists = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableJumpTwists; + set => (ScriptEditor as TR1ScriptEditor).EnableJumpTwists = value; } public bool EnableInvertedLook { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableInvertedLook; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableInvertedLook = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableInvertedLook; + set => (ScriptEditor as TR1ScriptEditor).EnableInvertedLook = value; } public int CameraSpeed { - get => ScriptEditor is TR1ScriptEditor tr1Editor ? tr1Editor.CameraSpeed : -1; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.CameraSpeed = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).CameraSpeed; + set => (ScriptEditor as TR1ScriptEditor).CameraSpeed = value; } public bool EnableSwingCancel { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableSwingCancel; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableSwingCancel = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableSwingCancel; + set => (ScriptEditor as TR1ScriptEditor).EnableSwingCancel = value; } public bool EnableTr2Jumping { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.EnableTr2Jumping; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.EnableTr2Jumping = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).EnableTr2Jumping; + set => (ScriptEditor as TR1ScriptEditor).EnableTr2Jumping = value; } public bool FixBearAi { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.FixBearAi; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.FixBearAi = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).FixBearAi; + set => (ScriptEditor as TR1ScriptEditor).FixBearAi = value; } public bool LoadCurrentMusic { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.LoadCurrentMusic; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.LoadCurrentMusic = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).LoadCurrentMusic; + set => (ScriptEditor as TR1ScriptEditor).LoadCurrentMusic = value; } public bool LoadMusicTriggers { - get => ScriptEditor is TR1ScriptEditor tr1Editor && tr1Editor.LoadMusicTriggers; - set - { - if (ScriptEditor is TR1ScriptEditor tr1Editor) - { - tr1Editor.LoadMusicTriggers = value; - } - } + get => (ScriptEditor as TR1ScriptEditor).LoadMusicTriggers; + set => (ScriptEditor as TR1ScriptEditor).LoadMusicTriggers = value; + } + + public bool ConvertDroppedGuns + { + get => (ScriptEditor as TR1ScriptEditor).ConvertDroppedGuns; + set => (ScriptEditor as TR1ScriptEditor).ConvertDroppedGuns = value; + } + + public bool EnableUwRoll + { + get => (ScriptEditor as TR1ScriptEditor).EnableUwRoll; + set => (ScriptEditor as TR1ScriptEditor).EnableUwRoll = value; + } + + public bool EnableEidosLogo + { + get => (ScriptEditor as TR1ScriptEditor).EnableEidosLogo; + set => (ScriptEditor as TR1ScriptEditor).EnableEidosLogo = value; + } + + public bool EnableBuffering + { + get => (ScriptEditor as TR1ScriptEditor).EnableBuffering; + set => (ScriptEditor as TR1ScriptEditor).EnableBuffering = value; + } + + public bool EnableLeanJumping + { + get => (ScriptEditor as TR1ScriptEditor).EnableLeanJumping; + set => (ScriptEditor as TR1ScriptEditor).EnableLeanJumping = value; + } + + public bool EnableConsole + { + get => (ScriptEditor as TR1ScriptEditor).EnableConsole; + set => (ScriptEditor as TR1ScriptEditor).EnableConsole = value; } #endregion diff --git a/TRRandomizerCore/TRRandomizerType.cs b/TRRandomizerCore/TRRandomizerType.cs index c65ae4fc9..f700b5f8b 100644 --- a/TRRandomizerCore/TRRandomizerType.cs +++ b/TRRandomizerCore/TRRandomizerType.cs @@ -20,7 +20,7 @@ public enum TRRandomizerType Outfit, Text, Environment, - Health, // Distinct from ammoless in Tomb1Main + Health, // Distinct from ammoless in TR1X Weather, // Individual settings diff --git a/TRRandomizerCore/TRVersionSupport.cs b/TRRandomizerCore/TRVersionSupport.cs index 171dc9881..3f162e7fc 100644 --- a/TRRandomizerCore/TRVersionSupport.cs +++ b/TRRandomizerCore/TRVersionSupport.cs @@ -6,9 +6,14 @@ internal class TRVersionSupport { private static readonly List _tr1Types = new() { + TRRandomizerType.AmbientTracks, + TRRandomizerType.Ammoless, TRRandomizerType.AtlanteanEggBehaviour, TRRandomizerType.Audio, + TRRandomizerType.Braid, TRRandomizerType.ChallengeRooms, + TRRandomizerType.ClonedEnemies, + TRRandomizerType.DisableDemos, TRRandomizerType.DynamicTextures, TRRandomizerType.DynamicEnemyTextures, TRRandomizerType.Enemy, @@ -19,42 +24,33 @@ internal class TRVersionSupport TRRandomizerType.GlitchedSecrets, TRRandomizerType.HardEnvironment, TRRandomizerType.HardSecrets, + TRRandomizerType.Health, TRRandomizerType.HiddenEnemies, TRRandomizerType.Item, TRRandomizerType.ItemSprite, TRRandomizerType.KeyItems, TRRandomizerType.KeyItemTextures, TRRandomizerType.LarsonBehaviour, + TRRandomizerType.LevelSequence, + TRRandomizerType.Mediless, TRRandomizerType.MeshSwaps, TRRandomizerType.NightMode, TRRandomizerType.Outfit, TRRandomizerType.ReturnPaths, TRRandomizerType.RewardRooms, TRRandomizerType.Secret, + TRRandomizerType.SecretCount, TRRandomizerType.SecretModels, TRRandomizerType.SecretReward, TRRandomizerType.SecretTextures, TRRandomizerType.ShortcutFixes, TRRandomizerType.SFX, TRRandomizerType.StartPosition, - TRRandomizerType.Traps, - TRRandomizerType.Texture - }; - - private static readonly List _tr1MainTypes = new() - { - TRRandomizerType.AmbientTracks, - TRRandomizerType.Ammoless, - TRRandomizerType.Braid, - TRRandomizerType.ClonedEnemies, - TRRandomizerType.DisableDemos, - TRRandomizerType.Health, - TRRandomizerType.LevelSequence, - TRRandomizerType.Mediless, - TRRandomizerType.SecretCount, TRRandomizerType.Text, + TRRandomizerType.Texture, + TRRandomizerType.Traps, TRRandomizerType.Unarmed, - TRRandomizerType.WaterColour + TRRandomizerType.WaterColour, }; private static readonly List _tr2Types = new() @@ -139,16 +135,15 @@ internal class TRVersionSupport private static readonly Dictionary _supportedTypes = new() { - [TRVersion.TR1] = new TRVersionSupportGroup + [TRVersion.TR1] = new() { - DefaultSupport = _tr1Types, - PatchSupport = _tr1MainTypes + DefaultSupport = _tr1Types }, - [TRVersion.TR2] = new TRVersionSupportGroup + [TRVersion.TR2] = new() { DefaultSupport = _tr2Types }, - [TRVersion.TR3] = new TRVersionSupportGroup + [TRVersion.TR3] = new() { DefaultSupport = _tr3Types, PatchSupport = _tr3MainTypes @@ -157,9 +152,9 @@ internal class TRVersionSupport private static readonly Dictionary> _versionExes = new() { - [TRVersion.TR1] = new List { "Tomb1Main.exe", "tombati.exe" }, - [TRVersion.TR2] = new List { "Tomb2.exe" }, - [TRVersion.TR3] = new List { "Tomb3.exe" } + [TRVersion.TR1] = new() { "TR1X.exe" }, + [TRVersion.TR2] = new() { "Tomb2.exe" }, + [TRVersion.TR3] = new() { "Tomb3.exe" } }; public static bool IsRandomizationSupported(TREdition edition) diff --git a/TRRandomizerCore/Utilities/TR1EnemyUtilities.cs b/TRRandomizerCore/Utilities/TR1EnemyUtilities.cs index 1649ee9a5..8ff77e776 100644 --- a/TRRandomizerCore/Utilities/TR1EnemyUtilities.cs +++ b/TRRandomizerCore/Utilities/TR1EnemyUtilities.cs @@ -5,6 +5,7 @@ using TRLevelControl.Helpers; using TRLevelControl.Model; using TRRandomizerCore.Helpers; +using TRRandomizerCore.Levels; namespace TRRandomizerCore.Utilities; @@ -111,18 +112,9 @@ public static Dictionary> PrepareEnemyGameTracker(RandoDif return tracker; } - public static bool IsEnemySupported(string lvlName, TR1Type entity, RandoDifficulty difficulty, bool isTomb1Main) + public static bool IsEnemySupported(string lvlName, TR1Type entity, RandoDifficulty difficulty) { - bool supported; - if (!isTomb1Main && _atiEnemyRestrictions.ContainsKey(entity)) - { - supported = _atiEnemyRestrictions[entity] == lvlName; - } - else - { - supported = IsEnemySupported(lvlName, entity, _unsupportedEnemiesTechnical); - } - + bool supported = IsEnemySupported(lvlName, entity, _unsupportedEnemiesTechnical); if (difficulty == RandoDifficulty.Default) { // a level may exist in both technical and difficulty dicts, so we check both @@ -341,99 +333,99 @@ public static RestrictedEnemyGroup GetRestrictedEnemyGroup(string lvlName, TR1Ty private static readonly Dictionary> _restrictedEnemyGroupCounts = new() { - [TR1LevelNames.CAVES] = new List + [TR1LevelNames.CAVES] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 5, Enemies = _allAtlanteans } }, - [TR1LevelNames.VILCABAMBA] = new List + [TR1LevelNames.VILCABAMBA] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 10, Enemies = _allAtlanteans } }, - [TR1LevelNames.VALLEY] = new List + [TR1LevelNames.VALLEY] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 5, Enemies = _allAtlanteans } }, - [TR1LevelNames.QUALOPEC] = new List + [TR1LevelNames.QUALOPEC] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 0, Enemies = new List { TR1Type.Larson } }, - new RestrictedEnemyGroup + new() { MaximumCount = 3, Enemies = _allAtlanteans } }, - [TR1LevelNames.FOLLY] = new List + [TR1LevelNames.FOLLY] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 10, Enemies = _allAtlanteans } }, - [TR1LevelNames.COLOSSEUM] = new List + [TR1LevelNames.COLOSSEUM] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 11, Enemies = _allAtlanteans } }, - [TR1LevelNames.MIDAS] = new List + [TR1LevelNames.MIDAS] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 15, Enemies = _allAtlanteans }, - new RestrictedEnemyGroup + new() { MaximumCount = 10, - Enemies = new List { TR1Type.TRex } + Enemies = new() { TR1Type.TRex } } }, - [TR1LevelNames.CISTERN] = new List + [TR1LevelNames.CISTERN] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 13, Enemies = _allAtlanteans } }, - [TR1LevelNames.TIHOCAN] = new List + [TR1LevelNames.TIHOCAN] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 9, Enemies = _allAtlanteans } }, - [TR1LevelNames.KHAMOON] = new List + [TR1LevelNames.KHAMOON] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 6, Enemies = _allAtlanteans } }, - [TR1LevelNames.OBELISK] = new List + [TR1LevelNames.OBELISK] = new() { - new RestrictedEnemyGroup + new() { MaximumCount = 6, Enemies = _allAtlanteans @@ -441,18 +433,11 @@ public static RestrictedEnemyGroup GetRestrictedEnemyGroup(string lvlName, TR1Ty }, }; - // These enemies are unsupported due to technical reasons, NOT difficulty reasons (T1M only). + // These enemies are unsupported due to technical reasons, NOT difficulty reasons. private static readonly Dictionary> _unsupportedEnemiesTechnical = new() { }; - // SkaterBoy and Natla can appear only in their OG levels in TombATI. - private static readonly Dictionary _atiEnemyRestrictions = new() - { - [TR1Type.SkateboardKid] = TR1LevelNames.MINES, - [TR1Type.Natla] = TR1LevelNames.PYRAMID, - }; - private static readonly Dictionary> _unsupportedEnemiesDefault = new() { [TR1LevelNames.QUALOPEC] = new List @@ -562,6 +547,57 @@ static TR1EnemyUtilities() File.ReadAllText(@"Resources\TR1\Restrictions\enemy_restrictions_technical.json") ); } + + public static ushort AtlanteanToCodeBits(TR1Type atlantean) + { + return atlantean switch + { + TR1Type.ShootingAtlantean_N => 1, + TR1Type.Centaur => 2, + TR1Type.Adam => 4, + TR1Type.NonShootingAtlantean_N => 8, + _ => 0, + }; + } + + public static TR1Type CodeBitsToAtlantean(ushort codeBits) + { + return codeBits switch + { + 1 => TR1Type.ShootingAtlantean_N, + 2 => TR1Type.Centaur, + 4 => TR1Type.Adam, + 8 => TR1Type.NonShootingAtlantean_N, + _ => TR1Type.FlyingAtlantean, + }; + } + + public static bool IsEmptyEgg(TR1Entity entity, TR1CombinedLevel level) + { + if (!TR1TypeUtilities.IsEggType(entity.TypeID)) + { + return false; + } + + TR1Type type = CodeBitsToAtlantean(entity.CodeBits); + return Array.Find(level.Data.Models, m => m.ID == (uint)type) == null; + } + + public static bool CanDropItems(TR1Entity entity, TR1CombinedLevel level, FDControl floorData) + { + if (IsEmptyEgg(entity, level)) + { + return false; + } + + if (entity.TypeID == TR1Type.Pierre) + { + return FDUtilities.GetEntityTriggers(floorData, level.Data.Entities.IndexOf(entity)) + .All(t => t.TrigSetup.OneShot); + } + + return TR1TypeUtilities.IsEnemyType(entity.TypeID); + } } public class RestrictedEnemyGroup diff --git a/TRRandomizerView/Controls/EditorControl.xaml b/TRRandomizerView/Controls/EditorControl.xaml index af19e4d6b..0c6ded335 100644 --- a/TRRandomizerView/Controls/EditorControl.xaml +++ b/TRRandomizerView/Controls/EditorControl.xaml @@ -616,17 +616,17 @@ Command="cmds:WindowCommands.OpenGlobalSettingsCommand"/>