Skip to content

Commit

Permalink
LostArtefacts#327 Killable Chickens
Browse files Browse the repository at this point in the history
Chicken behaviour can now be default, docile or unconditional - the latter meaning that they will be aggressive and the level won't end whey die. This replaced DocileBirdMonsters in the settings, which has been renamed to DocileWillard as it only applies to TR3 now.
  • Loading branch information
lahm86 committed Apr 25, 2022
1 parent 4492661 commit b8e5ac9
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 49 deletions.
9 changes: 6 additions & 3 deletions TRRandomizerCore/Editors/RandomizerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public class RandomizerSettings
public bool UseWireframeLadders { get; set; }
public bool CrossLevelEnemies { get; set; }
public bool ProtectMonks { get; set; }
public bool DocileBirdMonsters { get; set; }
public bool DocileWillard { get; set; }
public BirdMonsterBehaviour BirdMonsterBehaviour { get; set; }
public RandoDifficulty RandoEnemyDifficulty { get; set; }
public bool MaximiseDragonAppearance { get; set; }
public bool UseEnemyExclusions { get; set; }
Expand Down Expand Up @@ -131,7 +132,8 @@ public void ApplyConfig(Config config)
EnemySeed = config.GetInt(nameof(EnemySeed), defaultSeed);
CrossLevelEnemies = config.GetBool(nameof(CrossLevelEnemies), true);
ProtectMonks = config.GetBool(nameof(ProtectMonks), true);
DocileBirdMonsters = config.GetBool(nameof(DocileBirdMonsters));
DocileWillard = config.GetBool(nameof(DocileWillard));
BirdMonsterBehaviour = (BirdMonsterBehaviour)config.GetEnum(nameof(BirdMonsterBehaviour), typeof(BirdMonsterBehaviour), BirdMonsterBehaviour.Default);
RandoEnemyDifficulty = (RandoDifficulty)config.GetEnum(nameof(RandoEnemyDifficulty), typeof(RandoDifficulty), RandoDifficulty.Default);
MaximiseDragonAppearance = config.GetBool(nameof(MaximiseDragonAppearance));
UseEnemyExclusions = config.GetBool(nameof(UseEnemyExclusions));
Expand Down Expand Up @@ -235,7 +237,8 @@ public void StoreConfig(Config config)
config[nameof(EnemySeed)] = EnemySeed;
config[nameof(CrossLevelEnemies)] = CrossLevelEnemies;
config[nameof(ProtectMonks)] = ProtectMonks;
config[nameof(DocileBirdMonsters)] = DocileBirdMonsters;
config[nameof(DocileWillard)] = DocileWillard;
config[nameof(BirdMonsterBehaviour)] = BirdMonsterBehaviour;
config[nameof(RandoEnemyDifficulty)] = RandoEnemyDifficulty;
config[nameof(MaximiseDragonAppearance)] = MaximiseDragonAppearance;
config[nameof(ExcludedEnemies)] = string.Join(",", ExcludedEnemies);
Expand Down
7 changes: 7 additions & 0 deletions TRRandomizerCore/Helpers/BirdMonsterBehaviour.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TRRandomizerCore.Helpers
{
public enum BirdMonsterBehaviour
{
Default, Unconditional, Docile
}
}
33 changes: 22 additions & 11 deletions TRRandomizerCore/Randomizers/TR2/TR2EnemyRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private void RandomizeEnemiesCrossLevel()
}

// Track enemies whose counts across the game are restricted
_gameEnemyTracker = TR2EnemyUtilities.PrepareEnemyGameTracker(Settings.DocileBirdMonsters, Settings.RandoEnemyDifficulty);
_gameEnemyTracker = TR2EnemyUtilities.PrepareEnemyGameTracker(Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Docile, Settings.RandoEnemyDifficulty);

// #272 Selective enemy pool - convert the shorts in the settings to actual entity types
_excludedEnemies = Settings.UseEnemyExclusions ?
Expand Down Expand Up @@ -195,7 +195,7 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR2CombinedLevel level,
newEntities.Remove(TR2Entities.StickWieldingGoon1);
newEntities.Add(newGoon);

if (Settings.DocileBirdMonsters)
if (Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Docile)
{
newEntities.Remove(TR2Entities.MaskedGoon1);
newEntities.Add(TR2Entities.BirdMonster);
Expand Down Expand Up @@ -293,13 +293,13 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR2CombinedLevel level,
newEntities.Clear();
newEntities.AddRange(restrictedCombinations[_generator.Next(0, restrictedCombinations.Count)]);
}
while (Settings.DocileBirdMonsters && newEntities.Contains(TR2Entities.BirdMonster) && chickenGuisers.All(g => newEntities.Contains(g))
while (Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Docile && newEntities.Contains(TR2Entities.BirdMonster) && chickenGuisers.All(g => newEntities.Contains(g))
|| (newEntities.Any(_excludedEnemies.Contains) && restrictedCombinations.Any(c => !c.Any(_excludedEnemies.Contains))));
break;
}

// If it's the chicken in HSH but we're not using docile, we don't want it ending the level
if (!Settings.DocileBirdMonsters && entity == TR2Entities.BirdMonster && level.Is(TR2LevelNames.HOME) && allEnemies.Except(newEntities).Count() > 1)
// If it's the chicken in HSH with default behaviour, we don't want it ending the level
if (Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Default && entity == TR2Entities.BirdMonster && level.Is(TR2LevelNames.HOME) && allEnemies.Except(newEntities).Count() > 1)
{
continue;
}
Expand Down Expand Up @@ -334,7 +334,7 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR2CombinedLevel level,
{
// #144 We can include docile chickens provided we aren't including everything
// that can be disguised as a chicken.
if (Settings.DocileBirdMonsters)
if (Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Docile)
{
bool guisersAvailable = !chickenGuisers.All(g => newEntities.Contains(g));
// If the selected entity is the chicken, it can be added provided there are
Expand Down Expand Up @@ -381,7 +381,7 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR2CombinedLevel level,
}

// #144 Decide at this point who will be guising unless it has already been decided above (e.g. HSH)
if (Settings.DocileBirdMonsters && newEntities.Contains(TR2Entities.BirdMonster) && chickenGuiser == TR2Entities.BirdMonster)
if (Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Docile && newEntities.Contains(TR2Entities.BirdMonster) && chickenGuiser == TR2Entities.BirdMonster)
{
int guiserIndex = chickenGuisers.FindIndex(g => !newEntities.Contains(g));
if (guiserIndex != -1)
Expand Down Expand Up @@ -450,7 +450,7 @@ private void RandomizeEnemiesNatively(TR2CombinedLevel level)
List<TR2Entities> droppableEnemies = TR2EntityUtilities.DroppableEnemyTypes()[level.Name];
List<TR2Entities> waterEnemies = TR2EntityUtilities.FilterWaterEnemies(availableEnemyTypes);

if (Settings.DocileBirdMonsters && level.Is(TR2LevelNames.CHICKEN))
if (Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Docile && level.Is(TR2LevelNames.CHICKEN))
{
DisguiseEntity(level, TR2Entities.MaskedGoon1, TR2Entities.BirdMonster);
}
Expand Down Expand Up @@ -738,7 +738,7 @@ private void RandomizeEnemies(TR2CombinedLevel level, EnemyRandomizationCollecti

// #144 Disguise something as the Chicken. Pre-checks will have been done to ensure
// the guiser is suitable for the level.
if (Settings.DocileBirdMonsters && newEntityType == TR2Entities.BirdMonster)
if (Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Docile && newEntityType == TR2Entities.BirdMonster)
{
newEntityType = enemies.BirdMonsterGuiser;
}
Expand Down Expand Up @@ -815,6 +815,17 @@ private void RandomizeEnemies(TR2CombinedLevel level, EnemyRandomizationCollecti
}

RandomizeEnemyMeshes(level, enemies);

if (Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Unconditional && !level.Is(TR2LevelNames.CHICKEN))
{
TRModel model = Array.Find(level.Data.Models, m => m.ID == (uint)TR2Entities.BirdMonster);
if (model != null)
{
// #327 Trick the game into never reaching the final frame of the death animation.
// This results in a very abrupt death but avoids the level ending.
level.Data.Animations[model.Animation + 20].FrameEnd = level.Data.Animations[model.Animation + 19].FrameEnd;
}
}
}

private void LimitSkidooEntities(TR2CombinedLevel level)
Expand Down Expand Up @@ -877,7 +888,7 @@ private void RandomizeEnemyMeshes(TR2CombinedLevel level, EnemyRandomizationColl

List<TR2Entities> laraClones = new List<TR2Entities>();
const int chance = 2;
if (!Settings.DocileBirdMonsters)
if (Settings.BirdMonsterBehaviour != BirdMonsterBehaviour.Docile)
{
AddRandomLaraClone(enemies, TR2Entities.MonkWithKnifeStick, laraClones, chance);
AddRandomLaraClone(enemies, TR2Entities.MonkWithLongStick, laraClones, chance);
Expand Down Expand Up @@ -1063,7 +1074,7 @@ internal void ApplyRandomization()
All = new List<TR2Entities>(importedCollection.EntitiesToImport)
};

if (_outer.Settings.DocileBirdMonsters && importedCollection.BirdMonsterGuiser != TR2Entities.BirdMonster)
if (_outer.Settings.BirdMonsterBehaviour == BirdMonsterBehaviour.Docile && importedCollection.BirdMonsterGuiser != TR2Entities.BirdMonster)
{
_outer.DisguiseEntity(level, importedCollection.BirdMonsterGuiser, TR2Entities.BirdMonster);
enemies.BirdMonsterGuiser = importedCollection.BirdMonsterGuiser;
Expand Down
2 changes: 1 addition & 1 deletion TRRandomizerCore/Randomizers/TR3/TR3EnemyRandomizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private EnemyTransportCollection SelectCrossLevelEnemies(TR3CombinedLevel level)
}
}

if (!Settings.DocileBirdMonsters || Settings.OneEnemyMode || Settings.IncludedEnemies.Count < newEntities.Capacity)
if (!Settings.DocileWillard || Settings.OneEnemyMode || Settings.IncludedEnemies.Count < newEntities.Capacity)
{
// Willie isn't excludable in his own right because supporting a Willie-only game is impossible
allEnemies.Remove(TR3Entities.Willie);
Expand Down
16 changes: 13 additions & 3 deletions TRRandomizerCore/TRRandomizerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public class TRRandomizerController
[TRVersion.TR2] = new List<TRRandomizerType>
{
TRRandomizerType.GlobeDisplay, TRRandomizerType.RewardRooms, TRRandomizerType.VFX
},
[TRVersion.TR3] = new List<TRRandomizerType>
{
TRRandomizerType.BirdMonsterBehaviour
}
};

Expand Down Expand Up @@ -443,10 +447,16 @@ public bool ProtectMonks
set => LevelRandomizer.ProtectMonks = value;
}

public bool DocileBirdMonsters
public bool DocileWillard
{
get => LevelRandomizer.DocileWillard;
set => LevelRandomizer.DocileWillard = value;
}

public BirdMonsterBehaviour BirdMonsterBehaviour
{
get => LevelRandomizer.DocileBirdMonsters;
set => LevelRandomizer.DocileBirdMonsters = value;
get => LevelRandomizer.BirdMonsterBehaviour;
set => LevelRandomizer.BirdMonsterBehaviour = value;
}

public RandoDifficulty RandoEnemyDifficulty
Expand Down
3 changes: 2 additions & 1 deletion TRRandomizerCore/TRRandomizerType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum TRRandomizerType
GlobeDisplay,
RewardRooms,
VFX,
MaximumDragons
MaximumDragons,
BirdMonsterBehaviour
}
}
1 change: 1 addition & 0 deletions TRRandomizerView/Controls/EditorControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@
BoolItemsSource="{Binding Data.EnemyBoolItemControls, Source={StaticResource proxy}}"
HasBoolItems="True"
HasDifficulty="True"
HasBirdMonsterBehaviour="{Binding Data.IsBirdMonsterBehaviourTypeSupported, Source={StaticResource proxy}}"
ControllerProxy="{Binding Data, Source={StaticResource proxy}}">
</windows:AdvancedWindow>
</ctrl:ManagedSeedAdvancedControl.AdvancedWindowToOpen>
Expand Down
46 changes: 28 additions & 18 deletions TRRandomizerView/Model/ControllerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ControllerOptions : INotifyPropertyChanged

private BoolItemControlClass _isHardSecrets, _allowGlitched, _useRewardRoomCameras;
private BoolItemControlClass _includeKeyItems;
private BoolItemControlClass _crossLevelEnemies, _protectMonks, _docileBirdMonsters, _maximiseDragonAppearance;
private BoolItemControlClass _crossLevelEnemies, _protectMonks, _docileWillard, _maximiseDragonAppearance;
private BoolItemControlClass _persistTextures, _retainKeySpriteTextures, _retainSecretSpriteTextures;
private BoolItemControlClass _includeBlankTracks, _changeTriggerTracks, _separateSecretTracks, _changeWeaponSFX, _changeCrashSFX, _changeEnemySFX, _linkCreatureSFX;
private BoolItemControlClass _persistOutfits, _removeRobeDagger;
Expand Down Expand Up @@ -66,6 +66,7 @@ public class ControllerOptions : INotifyPropertyChanged
private RandoDifficulty _randoEnemyDifficulty;
private ItemDifficulty _randoItemDifficulty;
private GlobeDisplayOption _globeDisplayOption;
private BirdMonsterBehaviour _birdMonsterBehaviour;

private Language[] _availableLanguages;
private Language _gameStringLanguage;
Expand Down Expand Up @@ -976,12 +977,22 @@ public BoolItemControlClass UseRewardRoomCameras
}
}

public BoolItemControlClass DocileBirdMonsters
public BoolItemControlClass DocileWillard
{
get => _docileBirdMonsters;
get => _docileWillard;
set
{
_docileBirdMonsters = value;
_docileWillard = value;
FirePropertyChanged();
}
}

public BirdMonsterBehaviour BirdMonsterBehaviour
{
get => _birdMonsterBehaviour;
set
{
_birdMonsterBehaviour = value;
FirePropertyChanged();
}
}
Expand Down Expand Up @@ -1292,12 +1303,12 @@ public ControllerOptions()
Description = "Allow enemy types to appear in any level."
};
BindingOperations.SetBinding(CrossLevelEnemies, BoolItemControlClass.IsActiveProperty, randomizeEnemiesBinding);
DocileBirdMonsters = new BoolItemControlClass()
DocileWillard = new BoolItemControlClass()
{
Title = "Enable docile bird monsters",
Description = "Randomized bird monsters will not initiate on Lara and will not end the level upon death."
Title = "Enable docile Willard",
Description = "Willard can appear in levels other than Meteorite Cavern but will not attack Lara unless she gets too close."
};
BindingOperations.SetBinding(DocileBirdMonsters, BoolItemControlClass.IsActiveProperty, randomizeEnemiesBinding);
BindingOperations.SetBinding(DocileWillard, BoolItemControlClass.IsActiveProperty, randomizeEnemiesBinding);
ProtectMonks = new BoolItemControlClass()
{
Title = "Avoid having to kill allies",
Expand All @@ -1306,7 +1317,7 @@ public ControllerOptions()
BindingOperations.SetBinding(ProtectMonks, BoolItemControlClass.IsActiveProperty, randomizeEnemiesBinding);
MaximiseDragonAppearance = new BoolItemControlClass
{
Title = "Maximise dragon spawns",
Title = "Maximize dragon spawns",
Description = "Make a best effort attempt to have as many dragons as possible in the game."
};
BindingOperations.SetBinding(MaximiseDragonAppearance, BoolItemControlClass.IsActiveProperty, randomizeEnemiesBinding);
Expand Down Expand Up @@ -1448,7 +1459,7 @@ public ControllerOptions()
};
EnemyBoolItemControls = new List<BoolItemControlClass>()
{
_crossLevelEnemies, _docileBirdMonsters, _protectMonks, _maximiseDragonAppearance
_crossLevelEnemies, _docileWillard, _protectMonks, _maximiseDragonAppearance
};
TextureBoolItemControls = new List<BoolItemControlClass>()
{
Expand Down Expand Up @@ -1487,13 +1498,9 @@ private void AdjustAvailableOptions()

_useRewardRoomCameras.IsAvailable = IsRewardRoomsTypeSupported;

if (IsRewardRoomsTypeSupported) // i.e. IsTR3 - should make a TR version checker for the UI
{
DocileBirdMonsters.Title = "Enable docile Willard";
DocileBirdMonsters.Description = "Willard can appear in levels other than Meteorite Cavern but will not attack Lara unless she gets too close.";
}

_maximiseDragonAppearance.IsAvailable = IsOutfitDaggerSupported;

_docileWillard.IsAvailable = !IsBirdMonsterBehaviourTypeSupported;
}

public void Load(TRRandomizerController controller)
Expand Down Expand Up @@ -1562,7 +1569,8 @@ public void Load(TRRandomizerController controller)
EnemySeed = _controller.EnemySeed;
CrossLevelEnemies.Value = _controller.CrossLevelEnemies;
ProtectMonks.Value = _controller.ProtectMonks;
DocileBirdMonsters.Value = _controller.DocileBirdMonsters;
DocileWillard.Value = _controller.DocileWillard;
BirdMonsterBehaviour = _controller.BirdMonsterBehaviour;
MaximiseDragonAppearance.Value = _controller.MaximiseDragonAppearance;
RandoEnemyDifficulty = _controller.RandoEnemyDifficulty;
UseEnemyExclusions = _controller.UseEnemyExclusions;
Expand Down Expand Up @@ -1838,7 +1846,8 @@ public void Save()
_controller.EnemySeed = EnemySeed;
_controller.CrossLevelEnemies = CrossLevelEnemies.Value;
_controller.ProtectMonks = ProtectMonks.Value;
_controller.DocileBirdMonsters = DocileBirdMonsters.Value;
_controller.DocileWillard = DocileWillard.Value;
_controller.BirdMonsterBehaviour = BirdMonsterBehaviour;
_controller.MaximiseDragonAppearance = MaximiseDragonAppearance.Value;
_controller.RandoEnemyDifficulty = RandoEnemyDifficulty;
_controller.UseEnemyExclusions = UseEnemyExclusions;
Expand Down Expand Up @@ -1927,6 +1936,7 @@ public void Unload()
public bool IsOutfitDaggerSupported => IsRandomizationSupported(TRRandomizerType.OutfitDagger);
public bool IsTextTypeSupported => IsRandomizationSupported(TRRandomizerType.Text);
public bool IsEnvironmentTypeSupported => IsRandomizationSupported(TRRandomizerType.Environment);
public bool IsBirdMonsterBehaviourTypeSupported => IsRandomizationSupported(TRRandomizerType.BirdMonsterBehaviour);

public bool IsDisableDemosTypeSupported => IsRandomizationSupported(TRRandomizerType.DisableDemos);

Expand Down
Loading

0 comments on commit b8e5ac9

Please sign in to comment.