Skip to content

Commit

Permalink
Added extended soundtrack support?
Browse files Browse the repository at this point in the history
  • Loading branch information
Vivelin committed Oct 2, 2021
1 parent 2746ce0 commit 14fdc0f
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 42 deletions.
68 changes: 68 additions & 0 deletions Randomizer.SMZ3/FileData/ALttPSoundtrack.cs
@@ -0,0 +1,68 @@
namespace Randomizer.SMZ3.FileData {
public enum ALttPSoundtrack : byte {
None = 0,
Title = 1,
LightWorldOverworld = 2,
Rain = 3,
Bunny = 4,
LightLostWoods = 5,
OpeningStoryCredits = 6,
KakarikoVillage = 7,
Mirror = 8,
DarkWorldOverworld = 9,
MasterSwordPedestal = 10,
FileSelect = 11,
GuardSummonedInKakarikoVillage = 12,
DarkDeathMountain = 13,
GuessingGame = 14,
HyruleCastle = 16,
LightWorldDungeon = 17,
Cave1 = 18,
BossKillFanfare = 19,
Sanctuary = 20,
BossFight = 21,
DarkWorldDungeon = 22,
FortuneTeller = 23,
Cave2 = 24,
ZeldaRescued = 25,
CrystalGet = 26,
FairyFountain = 27,
GanonsTheme = 28,
GanonAppears = 29,
GanonInPyramid = 30,
GanonFight = 31,
TriforceRoom = 32,
TriumphantReturn = 33,
Credits = 34,
// Expanded soundtrack
DarkLostWoods = 15,
LightWorldOverworldPostPed = 60,
DarkWorldOverworldPostCrystals = 61,
EasternPalace = 35,
DesertPalace = 36,
AgahnimsTower = 37,
SwampPalace = 38,
PalaceOfDarkness = 39,
MiseryMire = 40,
SkullWoods = 41,
IcePalace = 42,
TowerOfHera = 43,
ThievesTown = 44,
TurtleRock = 45,
GanonsTower = 46,
GanonsTowerAscent = 59,
// Expanded soundtrack - boss fights
EasternPalaceBoss = 47,
DesertPalaceBoss = 48,
AgahnimsTowerBoss = 49,
SwampPalaceBoss = 50,
PalaceOfDarknessBoss = 51,
MiseryMireBoss = 52,
SkullWoodsBoss = 53,
IcePalaceBoss = 54,
TowerOfHeraBoss = 55,
ThievesTownBoss = 56,
TurtleRockBoss = 57,
GanonsTowerBoss = 58
}
}
52 changes: 52 additions & 0 deletions Randomizer.SMZ3/FileData/DungeonMusicAddresses.cs
@@ -0,0 +1,52 @@
namespace Randomizer.SMZ3.FileData {
public static class DungeonMusicAddresses {
public static readonly int[] EasternPalace = new[] {
0x02D59A
};
public static readonly int[] DesertPalace = new[] {
0x02D59B,
0x02D59C,
0x02D59D,
0x02D59E
};
public static readonly int[] TowerOfHera = new[] {
0x02D5C5,
0x02907A,
0x028B8C
};
public static readonly int[] PalaceOfDarkness = new[] {
0x02D5B8
};
public static readonly int[] SwampPalace = new[] {
0x02D5B7
};
public static readonly int[] SkullWoods = new[] {
0x02D5BA,
0x02D5BB,
0x02D5BC,
0x02D5BD,
0x02D608,
0x02D609,
0x02D60A,
0x02D60B
};
public static readonly int[] ThievesTown = new[] {
0x02D5C6
};
public static readonly int[] IcePalace = new[] {
0x02D5BF
};
public static readonly int[] MiseryMire = new[] {
0x02D5B9
};
public static readonly int[] TurtleRock = new[] {
0x02D5C7,
0x02D5A7,
0x02D5AA,
0x02D5AB
};
public static readonly int[] GanonsTower = new[] {
0x02D5C9
};
}
}
67 changes: 25 additions & 42 deletions Randomizer.SMZ3/FileData/Patch.cs
Expand Up @@ -75,6 +75,14 @@ public Patch(World myWorld, List<World> allWorlds, string seedGuid, int seed, Ra
return patches.ToDictionary(x => x.offset, x => x.bytes);
}

protected void AddPatch(int[] addresses, byte[] values)
{
foreach (var address in addresses)
{
patches.Add((Snes(address), values));
}
}

private static int Snes(int addr)
{
addr = addr switch
Expand Down Expand Up @@ -323,51 +331,26 @@ private byte GetZ3ItemId(Location location)

private void WriteDungeonMusic(bool keysanity)
{
if (!keysanity)
SetSoundtrack(DungeonMusicAddresses.EasternPalace, ALttPSoundtrack.EasternPalace);
SetSoundtrack(DungeonMusicAddresses.DesertPalace, ALttPSoundtrack.DesertPalace);
// SetSoundtrack(DungeonMusicAddresses.CastleTower,
// ALttPSoundtrack.AgahnimsTower);
SetSoundtrack(DungeonMusicAddresses.SwampPalace, ALttPSoundtrack.SwampPalace);
SetSoundtrack(DungeonMusicAddresses.PalaceOfDarkness, ALttPSoundtrack.PalaceOfDarkness);
SetSoundtrack(DungeonMusicAddresses.MiseryMire, ALttPSoundtrack.MiseryMire);
SetSoundtrack(DungeonMusicAddresses.SkullWoods, ALttPSoundtrack.SkullWoods);
SetSoundtrack(DungeonMusicAddresses.IcePalace, ALttPSoundtrack.IcePalace);
SetSoundtrack(DungeonMusicAddresses.TowerOfHera, ALttPSoundtrack.TowerOfHera);
SetSoundtrack(DungeonMusicAddresses.ThievesTown, ALttPSoundtrack.ThievesTown);
SetSoundtrack(DungeonMusicAddresses.TurtleRock, ALttPSoundtrack.TurtleRock);
SetSoundtrack(DungeonMusicAddresses.GanonsTower, ALttPSoundtrack.GanonsTower);

void SetSoundtrack(int[] addresses, ALttPSoundtrack track)
{
var regions = myWorld.Regions.OfType<IHasReward>();
IEnumerable<byte> music;
var pendantRegions = regions.Where(x => new[] { PendantGreen, PendantNonGreen }.Contains(x.Reward));
var crystalRegions = regions.Where(x => new[] { CrystalBlue, CrystalRed }.Contains(x.Reward));
regions = pendantRegions.Concat(crystalRegions);
music = new byte[] {
0x11, 0x11, 0x11, 0x16, 0x16,
0x16, 0x16, 0x16, 0x16, 0x16,
};
patches.AddRange(MusicPatches(regions, music));
AddPatch(addresses, new byte[] { (byte)track });
}
}

private IEnumerable<byte> RandomDungeonMusic()
{
while (true) yield return rnd.Next(2) == 0 ? (byte)0x11 : (byte)0x16;
}

private IEnumerable<(int, byte[])> MusicPatches(IEnumerable<IHasReward> regions, IEnumerable<byte> music)
{
var addresses = regions.Select(MusicAddresses);
var associations = addresses.Zip(music, (a, b) => (a, b));
return associations.SelectMany(x => x.a.Select(i => (Snes(i), new byte[] { x.b })));
}

private int[] MusicAddresses(IHasReward region)
{
return region switch
{
EasternPalace _ => new[] { 0x2D59A },
DesertPalace _ => new[] { 0x2D59B, 0x2D59C, 0x2D59D, 0x2D59E },
TowerOfHera _ => new[] { 0x2D5C5, 0x2907A, 0x28B8C },
PalaceOfDarkness _ => new[] { 0x2D5B8 },
SwampPalace _ => new[] { 0x2D5B7 },
SkullWoods _ => new[] { 0x2D5BA, 0x2D5BB, 0x2D5BC, 0x2D5BD, 0x2D608, 0x2D609, 0x2D60A, 0x2D60B },
ThievesTown _ => new[] { 0x2D5C6 },
IcePalace _ => new[] { 0x2D5BF },
MiseryMire _ => new[] { 0x2D5B9 },
TurtleRock _ => new[] { 0x2D5C7, 0x2D5A7, 0x2D5AA, 0x2D5AB },
var x => throw new InvalidOperationException($"Region {x} should not be a dungeon music region"),
};
}

private void WritePrizeShuffle()
{
const int prizePackItems = 56;
Expand Down Expand Up @@ -558,7 +541,7 @@ private void WriteTexts(Config config)
stringTable.SetGanonFirstPhaseText(ganon);

// Todo: Verify these two are correct if ganon invincible patch is
// ever added ganon_fall_in_alt in v30
// ever added ganon_fall_in_alt in v30
var ganonFirstPhaseInvincible = "You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!";
patches.Add((Snes(0x309100), Dialog.Simple(ganonFirstPhaseInvincible)));

Expand Down

0 comments on commit 14fdc0f

Please sign in to comment.