Skip to content

Commit

Permalink
(SET) Enemy Duplication
Browse files Browse the repository at this point in the history
Because I saw a Metroid Prime mod that duped every enemy and was inspired.
  • Loading branch information
Knuxfan24 committed Sep 3, 2022
1 parent ec046f5 commit c92de47
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
3 changes: 2 additions & 1 deletion MarathonRandomiser/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
xmlns:hc="https://handyorg.github.io/handycontrol"
x:Class="MarathonRandomiser.MainWindow"
mc:Ignorable="d"
Title="Sonic '06 Randomiser Suite" Height="720" Width="1520" MinHeight="720" MinWidth="1520">
Title="Sonic '06 Randomiser Suite" Height="736" Width="1520" MinHeight="736" MinWidth="1520">
<Grid Background="#FF181818">
<hc:TabControl Style="{StaticResource TabControlInLine}" Margin="0,0,0,50" x:Name="TabControl_Main">
<!--General Tab-->
Expand Down Expand Up @@ -90,6 +90,7 @@
<Label x:Name="Label_SET_Jumpboards_Chance" Content="Chance:" Background="{x:Null}" BorderBrush="{x:Null}" IsEnabled="False"/>
<hc:NumericUpDown x:Name="NumericUpDown_SET_Jumpboards_Chance" Value="50" SelectionBrush="#FFB146C2" IsEnabled="False" Minimum="0"/>
<CheckBox x:Name="CheckBox_SET_PlacementShuffle" Content="Shuffle Object Positions" Foreground="Firebrick" Click="Dependency_CheckBox_Changed"/>
<CheckBox x:Name="CheckBox_SET_DoubleTrouble" Content="Duplicate Enemies" Foreground="Firebrick" Click="Dependency_CheckBox_Changed"/>
</StackPanel>

<!--Object Placement Configuration Toggles-->
Expand Down
13 changes: 11 additions & 2 deletions MarathonRandomiser/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,14 @@ private void Dependency_CheckBox_Changed(object sender, RoutedEventArgs e)
Label_SET_Jumpboards_Chance.IsEnabled = NewCheckedStatus;
NumericUpDown_SET_Jumpboards_Chance.IsEnabled = NewCheckedStatus;
break;
case "CheckBox_SET_PlacementShuffle":
CheckBox_SET_DoubleTrouble.IsEnabled = !NewCheckedStatus;
CheckBox_SET_DoubleTrouble.IsChecked = false;
break;
case "CheckBox_SET_DoubleTrouble":
CheckBox_SET_PlacementShuffle.IsEnabled = !NewCheckedStatus;
CheckBox_SET_PlacementShuffle.IsChecked = false;
break;

case "CheckBox_Event_Voices":
CheckBox_Event_Voices_Japanese.IsEnabled = NewCheckedStatus;
Expand Down Expand Up @@ -1766,10 +1774,11 @@ private async void Randomise(object sender, RoutedEventArgs e)
int setMaxDrawDistance = (int)NumericUpDown_SET_DrawDistance_Max.Value;
int setJumpboardsChance = (int)NumericUpDown_SET_Jumpboards_Chance.Value;
bool? setTransform = CheckBox_SET_PlacementShuffle.IsChecked;
bool? setDoubleTrouble = CheckBox_SET_DoubleTrouble.IsChecked;

// Check if we actually need to do SET stuff.
if (setEnemies == true || setBehaviour == true || setCharacters == true || setItemCapsules == true || setCommonProps == true || setPathProps == true || setHints == true || setDoors == true||
setDrawDistance == true || setCosmetic == true || setParticles == true || setJumpboards == true || setTransform == true)
setDrawDistance == true || setCosmetic == true || setParticles == true || setJumpboards == true || setTransform == true || setDoubleTrouble == true)
{
foreach (string archive in archives)
{
Expand All @@ -1784,7 +1793,7 @@ private async void Randomise(object sender, RoutedEventArgs e)
await Task.Run(() => ObjectPlacementRandomiser.Process(setFile, setEnemies, setEnemiesNoBosses, setBehaviour, setBehaviourNoEnforce, setCharacters, setItemCapsules,
setCommonProps, setPathProps, setHints, setDoors, setDrawDistance, setCosmetic, setParticles, setJumpboards, SetEnemies,
SetCharacters, SetItemCapsules, SetCommonProps, SetPathProps, SetHints, SetDoors, SetParticleBanks, setMinDrawDistance,
setMaxDrawDistance, setJumpboardsChance, setTransform, SetShuffleBlacklist));
setMaxDrawDistance, setJumpboardsChance, setTransform, SetShuffleBlacklist, setDoubleTrouble));
}

// Patch enemy luas if they need patching.
Expand Down
64 changes: 62 additions & 2 deletions MarathonRandomiser/Randomisers/ObjectPlacementRandomiser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ namespace MarathonRandomiser
{
internal class ObjectPlacementRandomiser
{
// Set up a list of duplicated enemy objects.
private static List<SetObject> DuplicatedEnemies = new();

/// <summary>
/// Process and randomise elements in a set file.
/// </summary>
Expand Down Expand Up @@ -40,14 +43,22 @@ internal class ObjectPlacementRandomiser
public static async Task Process(string setFile, bool? enemies, bool? enemiesNoBosses, bool? behaviour, bool? behaviourNoEnforce, bool? characters, bool? itemCapsules, bool? commonProps,
bool? pathProps, bool? hints, bool? doors, bool? drawDistance, bool? cosmetic, bool? particle, bool? jumpboards, List<string> SetEnemies,
List<string> SetCharacters, List<string> SetItemCapsules, List<string> SetCommonProps, List<string> SetPathProps, List<string> SetHints, List<string> SetDoors,
List<string> SetParticleBanks, int minDrawDistance, int maxDrawDistance, int jumpboardChance, bool? shuffleTransform, List<string> shuffleBlacklist)
List<string> SetParticleBanks, int minDrawDistance, int maxDrawDistance, int jumpboardChance, bool? shuffleTransform, List<string> shuffleBlacklist,
bool? doubleTrouble)
{
// Clear the enemy list.
DuplicatedEnemies.Clear();

// Set up position and rotation lists for the object shuffler.
List<Vector3> Positions = new();
List<Quaternion> Rotations = new();

// Load this set file.
using ObjectPlacement set = new(setFile);

// Set up the index of the object we're working with.
int objectIndex = 0;

// Loop through all the objects in this set file.
foreach (SetObject setObject in set.Data.Objects)
{
Expand All @@ -73,13 +84,15 @@ public static async Task Process(string setFile, bool? enemies, bool? enemiesNoB
// Check this object's type to see if we need to do anything with it.
switch (setObject.Type)
{
// Randomise enemy types and/or their behaviours if we need to.
// Randomise enemy types and/or their behaviours and/or dupe them if we need to.
case "enemy":
case "enemyextra":
if (enemies == true)
await Task.Run(() => EnemyTypeRandomiser(setObject, SetEnemies, enemiesNoBosses));
if (behaviour == true)
await Task.Run(() => EnemyBehaviourRandomiser(setObject, behaviourNoEnforce, enemiesNoBosses));
if (doubleTrouble == true)
await Task.Run(() => DuplicateEnemy(set, setObject, objectIndex));
break;

// Randomise character types if we need to.
Expand Down Expand Up @@ -141,6 +154,8 @@ public static async Task Process(string setFile, bool? enemies, bool? enemiesNoB

}

// Increment objectIndex.
objectIndex++;
}

// If we're using this cursed option, then shuffle positions and rotations around.
Expand Down Expand Up @@ -219,6 +234,10 @@ public static async Task Process(string setFile, bool? enemies, bool? enemiesNoB
}
}

// Add our duplicated enemies to the SET.
foreach (SetObject duplicatedEnemy in DuplicatedEnemies)
set.Data.Objects.Add(duplicatedEnemy);

// Save the updated set file.
set.Save();
}
Expand Down Expand Up @@ -428,6 +447,47 @@ static async Task EnemyBehaviourRandomiser(SetObject setObject, bool? dontEnforc
}
}

static async Task DuplicateEnemy(ObjectPlacement set, SetObject setObject, int objectIndex)
{
// Don't dupe the Egg Wyvern, as the boss completely breaks.
if (setObject.Parameters[0].Data.ToString() == "eWyvern")
return;

// Create our duplicated enemy.
SetObject newSetObject = new()
{
Name = $"{setObject.Name}_dupe",
Type = setObject.Type,
StartInactive = setObject.StartInactive,
DrawDistance = setObject.DrawDistance,
Rotation = setObject.Rotation,
Parameters = setObject.Parameters
};

// Offset the object positions.
if (setObject.Parameters[0].Data.ToString() != "eCerberus" && setObject.Parameters[0].Data.ToString() != "eGenesis" &&
setObject.Parameters[0].Data.ToString() != "firstiblis" && setObject.Parameters[0].Data.ToString() != "secondiblis" && setObject.Parameters[0].Data.ToString() != "thirdiblis" &&
setObject.Parameters[0].Data.ToString() != "firstmefiress" && setObject.Parameters[0].Data.ToString() != "secondmefiress" && setObject.Parameters[0].Data.ToString() != "kyozoress" &&
setObject.Parameters[0].Data.ToString() != "solaris01" && setObject.Parameters[0].Data.ToString() != "solaris02")
{
setObject.Position = new(setObject.Position.X + 250, setObject.Position.Y, setObject.Position.Z + 250);
newSetObject.Position = new(setObject.Position.X - 250, setObject.Position.Y, setObject.Position.Z - 250);
}
else
{
setObject.Position = new(setObject.Position.X + 1500, setObject.Position.Y, setObject.Position.Z + 1500);
newSetObject.Position = new(setObject.Position.X - 1500, setObject.Position.Y, setObject.Position.Z - 1500);
}

// Update any groups that use the original enemy.
foreach (var group in set.Data.Groups)
if (group.Objects.Contains((ulong)objectIndex))
group.Objects.Add((ulong)(set.Data.Objects.Count + DuplicatedEnemies.Count));

// Add this object to the list of objects to add in after the rest of the randomisation.
DuplicatedEnemies.Add(newSetObject);
}

/// <summary>
/// Randomises props that use values in Common.bin.
/// </summary>
Expand Down

0 comments on commit c92de47

Please sign in to comment.