private static void Randomize(int seed, string path, Flags flags, string encoded) { //Console.WriteLine("Seed: " + seed); string json = JsonConvert.SerializeObject(flags); if (string.IsNullOrWhiteSpace(encoded)) { encoded = flags.GetEncoded(); } else { flags.DecodeAndSet(encoded); json = JsonConvert.SerializeObject(flags); } Console.WriteLine("Flags JSON : " + json); Console.WriteLine("Flags Base64: " + encoded); StreamWriter spoilerWriter = new StreamWriter("spoiler.txt"); SpoilerLog spoilerLog = new SpoilerLog(spoilerWriter, flags.SpoilerLog); System.IO.File.AppendAllText(@"seed.txt", seed.ToString() + " " + encoded + Environment.NewLine); spoilerLog.WriteFlags(flags); var random = new Random(seed); var randomValues = new List <int>(); for (int i = 0; i < 50; i++) { randomValues.Add(random.Next()); } var ultimaData = new UltimaData(); IWorldMap worldMap = null; //WorldMapGenerateMap.PrintWorldMapInfo(path); if (flags.Overworld == 5) { worldMap = new WorldMapGenerateMap(spoilerLog); } else if (flags.Overworld == 1) { worldMap = new WorldMapUnchanged(spoilerLog); } else if (flags.Overworld == 2) { worldMap = new WorldMapShuffleLocations(spoilerLog); } worldMap.Load(path, randomValues[0], randomValues[1], randomValues[2], ultimaData); var avatar = new Avatar(spoilerLog); avatar.Load(path, ultimaData, worldMap, flags); var title = new Title(spoilerLog); title.Load(path, ultimaData); var talk = new Talk(spoilerLog); talk.Load(path); var dungeons = new Dungeons(spoilerLog); dungeons.Load(path, ultimaData, flags); var party = new Party(spoilerLog); party.Load(path, ultimaData); var towns = new Towns(spoilerLog); towns.Load(path, ultimaData); if (flags.Fixes) { spoilerLog.Add(SpoilerCategory.Fix, "Serpent Hold's Healer"); ultimaData.ShopLocations[ultimaData.LOC_SERPENT - 1][5] = 0x12; } worldMap.Randomize(ultimaData, new Random(randomValues[3]), new Random(randomValues[4])); dungeons.Randomize(new Random(randomValues[6]), flags); if (flags.ClothMap) { var clothMap = worldMap.ToClothMap(ultimaData, new Random(randomValues[5])); clothMap.SaveAsPng($"clothMap-{seed}.png"); new Process { StartInfo = new ProcessStartInfo($"clothMap-{seed}.png") { UseShellExecute = true } }.Start(); } if (flags.RandomizeSpells) { var recipes = new byte[2]; ultimaData.SpellsRecipes['r' - 'a'].Byte = 0; ultimaData.SpellsRecipes['g' - 'a'].Byte = 0; while (ultimaData.SpellsRecipes['r' - 'a'].Byte == 0) { random.NextBytes(recipes); ultimaData.SpellsRecipes['r' - 'a'].Byte = (byte)(recipes[0] | recipes[1]); } while (ultimaData.SpellsRecipes['g' - 'a'].Byte == 0) { random.NextBytes(recipes); ultimaData.SpellsRecipes['g' - 'a'].Byte = (byte)(recipes[0] | recipes[1]); } } if (!String.IsNullOrWhiteSpace(flags.SpellRemove)) { var arr = flags.SpellRemove.ToLower().ToArray(); for (int i = 0; i < arr.Length; i++) { if (arr[i] > 'z' || arr[i] < 'a') { throw new ArgumentException("spellRemove can only contain letters."); } ultimaData.SpellsRecipes[arr[i] - 'a'].Byte = 0; } } if (flags.StartingWeapons) { for (int charIdx = 0; charIdx < 8; charIdx++) { var selected = false; while (!selected) { // -1 so Mystic weapons and armors aren't included var weapon = random.Next(1, Party.AllowedWeaponsMask.Length - 1); //If weapon is allowed if ((Party.AllowedWeaponsMask[weapon] & (0x80 >> ultimaData.StartingCharacters[charIdx].Class)) != 0) { ultimaData.StartingCharacters[charIdx].Weapon = (ushort)weapon; selected = true; } } selected = false; while (!selected) { var armor = random.Next(1, Party.AllowedArmorMask.Length); //If weapon is allowed if ((Party.AllowedArmorMask[armor] & (0x80 >> ultimaData.StartingCharacters[charIdx].Class)) != 0) { ultimaData.StartingCharacters[charIdx].Armor = (ushort)armor; selected = true; } } } } //worldMap.TestAbyssEjection(); //Console.WriteLine(Talk.GetSextantText(ultimaData.LCB[0])); //for (int i = 0; i < 8; i++) //{ // ultimaData.StartingArmor[i] = Convert.ToUInt16(i + 10); //} //for (int i = 0; i < 16; i++) //{ // ultimaData.StartingWeapons[i] = Convert.ToUInt16(i + 10); //} //ultimaData.StartingFood = 2345 * 100 + 99; //ultimaData.StartingGold = 1337; //for (int i = 0; i < 4; i++) //{ // ultimaData.StartingEquipment[i] = Convert.ToUInt16(i + 10); //} //for (int i = 0; i < 8; i++) //{ // ultimaData.StartingReagents[i] = Convert.ToUInt16(i + 10); //} //for (int i = 0; i < 26; i++) //{ // ultimaData.StartingMixtures[i] = Convert.ToUInt16(i + 10); //} //ultimaData.StartingItems = 0XFFFF; if (flags.QuestItemPercentage > 0) { ushort ushortone = 1; ultimaData.StartingItems = 0; for (ushort i = 0; i < 16; i++) { if (random.Next(0, 100) < flags.QuestItemPercentage) { ultimaData.StartingItems |= (ushort)(ushortone << i); } } // Never have the skull destroyed ultimaData.StartingItems &= (ushort)(~(ushortone << 1)); // Don' pre-use bell, book and candle ultimaData.StartingItems &= (ushort)(~(ushortone << 10)); ultimaData.StartingItems &= (ushort)(~(ushortone << 11)); ultimaData.StartingItems &= (ushort)(~(ushortone << 12)); ultimaData.StartingRunes = 0; for (ushort i = 0; i < 8; i++) { if (random.Next(0, 100) < flags.QuestItemPercentage) { ultimaData.StartingRunes |= (byte)(1 << i); } } ultimaData.StartingStones = 0; for (ushort i = 0; i < 8; i++) { if (random.Next(0, 100) < flags.QuestItemPercentage) { ultimaData.StartingStones |= (byte)(1 << i); } } LogQuestItems(spoilerLog, ultimaData); } else { spoilerLog.Add(SpoilerCategory.Start, "No change to starting quest items."); } if (flags.Sextant) { ultimaData.StartingEquipment[3] = 0x01; } if (flags.KarmaSetPercentage > 0) { for (int virtue = 0; virtue < 8; virtue++) { if (random.Next(0, 100) < flags.KarmaSetPercentage) { ultimaData.StartingKarma[virtue] = (flags.KarmaValue.HasValue ? (byte)flags.KarmaValue.Value : (byte)random.Next(0, 100)); spoilerLog.Add(SpoilerCategory.Start, $"{ultimaData.ItemNames[virtue + 15]} karma at {ultimaData.StartingKarma[virtue]}"); } else { spoilerLog.Add(SpoilerCategory.Start, $"{ultimaData.ItemNames[virtue + 15]} karma unchanged."); } } } if (flags.Runes) { spoilerLog.Add(SpoilerCategory.Feature, $"Rune locations randomized"); var usedLocations = new List <byte>(); for (int i = UltimaData.ITEM_RUNE_HONESTY; i < 8 + UltimaData.ITEM_RUNE_HONESTY; i++) { var possibleOptions = ItemOptions.ItemToItemOptions[i].Where(x => !usedLocations.Contains(x.Item.Location)).ToList(); var selectedItemOption = possibleOptions[random.Next(0, possibleOptions.Count)]; ultimaData.Items[i].X = selectedItemOption.Item.X; ultimaData.Items[i].Y = selectedItemOption.Item.Y; ultimaData.Items[i].Location = selectedItemOption.Item.Location; ultimaData.ItemOptions.Add(i, selectedItemOption); usedLocations.Add(selectedItemOption.Item.Location); } } if (flags.Mantras) { int numberOfTwos = 3; int numberOfThrees = 4; int numberOfFours = 1; // Grab Sacrifice first since it is special var mantrasWithLimericks = talk.Mantras.Where(x => x.Limerick.Length > 0).ToList(); var sacrificeMantra = mantrasWithLimericks[random.Next(0, mantrasWithLimericks.Count)]; talk.Mantras.Remove(sacrificeMantra); if (sacrificeMantra.Text.Length == 2) { numberOfTwos--; } else if (sacrificeMantra.Text.Length == 3) { numberOfThrees--; } else if (sacrificeMantra.Text.Length == 4) { numberOfFours--; } var possibleTwos = talk.Mantras.Where(x => x.Text.Length == 2).ToList(); var possibleThrees = talk.Mantras.Where(x => x.Text.Length == 3).ToList(); var possibleFours = talk.Mantras.Where(x => x.Text.Length == 4).ToList(); var possibleMantras = new List <Mantra>(); for (int i = 0; i < numberOfTwos; i++) { var nextIdx = random.Next(0, possibleTwos.Count); possibleMantras.Add(possibleTwos[nextIdx]); possibleTwos.RemoveAt(nextIdx); } for (int i = 0; i < numberOfThrees; i++) { var nextIdx = random.Next(0, possibleThrees.Count); possibleMantras.Add(possibleThrees[nextIdx]); possibleThrees.RemoveAt(nextIdx); } for (int i = 0; i < numberOfFours; i++) { var nextIdx = random.Next(0, possibleFours.Count); possibleMantras.Add(possibleFours[nextIdx]); possibleFours.RemoveAt(nextIdx); } possibleMantras.Shuffle(random); possibleMantras.Insert(4, sacrificeMantra); talk.Mantras = possibleMantras; for (int i = 0; i < 8; i++) { ultimaData.Mantras[i] = talk.Mantras[i].Text.ToLower(); } } if (flags.WordOfPassage) { var selection = talk.WordsOfPassage[random.Next(0, talk.WordsOfPassage.Count)]; ultimaData.WordTruth = selection.Item1; ultimaData.WordLove = selection.Item2; ultimaData.WordCourage = selection.Item3; ultimaData.WordOfPassage = selection.Item1 + selection.Item2 + selection.Item3; } //ultimaData.StartingStones = 0XFF; //ultimaData.StartingRunes = 0XFF; title.Update(ultimaData, flags, encoded); talk.Update(ultimaData, avatar, flags, worldMap); avatar.Update(ultimaData, flags); dungeons.Update(ultimaData, flags); party.Update(ultimaData); towns.Update(ultimaData, flags); towns.Save(path); party.Save(path); dungeons.Save(path); title.Save(path); talk.Save(path); avatar.Save(path); worldMap.Save(path); if (flags.MiniMap) { var image = worldMap.ToImage(); image.SaveAsPng($"worldMap-{seed}.png"); //image = worldMap.ToHeightMapImage(); //if (image != null) //{ // image.SaveAsPng($"worldMap-hm-{seed}.png"); //} } spoilerWriter.Close(); //PrintWorldMapInfo(); }
public void Update(UltimaData data, Flags flags) { // Items for (var offset = 0; offset < data.Items.Count; offset++) { avatarBytes[AvatarOffset.ITEM_LOCATIONS_OFFSET + offset * 5] = data.Items[offset].Location; avatarBytes[AvatarOffset.ITEM_LOCATIONS_OFFSET + offset * 5 + 1] = data.Items[offset].X; avatarBytes[AvatarOffset.ITEM_LOCATIONS_OFFSET + offset * 5 + 2] = data.Items[offset].Y; } // Use these items at the entrance to the abyss avatarBytes[AvatarOffset.ITEM_USE_TRIGGER_BELL_X_OFFSET] = data.Dungeons[data.Dungeons.Count - 1].X; avatarBytes[AvatarOffset.ITEM_USE_TRIGGER_BELL_Y_OFFSET] = data.Dungeons[data.Dungeons.Count - 1].Y; avatarBytes[AvatarOffset.ITEM_USE_TRIGGER_BOOK_X_OFFSET] = data.Dungeons[data.Dungeons.Count - 1].X; avatarBytes[AvatarOffset.ITEM_USE_TRIGGER_BOOK_Y_OFFSET] = data.Dungeons[data.Dungeons.Count - 1].Y; avatarBytes[AvatarOffset.ITEM_USE_TRIGGER_CANDLE_X_OFFSET] = data.Dungeons[data.Dungeons.Count - 1].X; avatarBytes[AvatarOffset.ITEM_USE_TRIGGER_CANDLE_Y_OFFSET] = data.Dungeons[data.Dungeons.Count - 1].Y; avatarBytes[AvatarOffset.ITEM_USE_TRIGGER_SKULL_X_OFFSET] = data.Dungeons[data.Dungeons.Count - 1].X; avatarBytes[AvatarOffset.ITEM_USE_TRIGGER_SKULL_Y_OFFSET] = data.Dungeons[data.Dungeons.Count - 1].Y; ////throw in a lava to make it easy to find //for (int offset = 0; offset < 8; offset++) //{ // worldMapUlt[200, 200 + offset] = 76; //} // Moongates for (byte offset = 0; offset < data.Moongates.Count; offset++) { avatarBytes[AvatarOffset.MOONGATE_X_OFFSET + offset] = data.Moongates[offset].X; avatarBytes[AvatarOffset.MOONGATE_Y_OFFSET + offset] = data.Moongates[offset].Y; } avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_LCB - 1] = data.LCB[0].X; avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_LCB - 1] = data.LCB[0].Y; avatarBytes[AvatarOffset.DEATH_EXIT_X_OFFSET] = data.LCB[0].X; avatarBytes[AvatarOffset.DEATH_EXIT_Y_OFFSET] = data.LCB[0].Y; for (var offset = 0; offset < data.Castles.Count; offset++) { avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_CASTLES + offset] = data.Castles[offset].X; avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_CASTLES + offset] = data.Castles[offset].Y; } for (var offset = 0; offset < data.Towns.Count; offset++) { avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_TOWNS + offset - 1] = data.Towns[offset].X; avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_TOWNS + offset - 1] = data.Towns[offset].Y; } for (var offset = 0; offset < data.Shrines.Count; offset++) { // Skip Spirituality if (data.Shrines[offset] != null) { avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_SHRINES + offset - 1] = data.Shrines[offset].X; avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_SHRINES + offset - 1] = data.Shrines[offset].Y; } } for (var offset = 0; offset < data.Dungeons.Count; offset++) { avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_DUNGEONS + offset - 1] = data.Dungeons[offset].X; avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_DUNGEONS + offset - 1] = data.Dungeons[offset].Y; } avatarBytes[AvatarOffset.BALLOON_SPAWN_TRIGGER_X_OFFSET] = data.Dungeons[data.Dungeons.Count - 2].X; avatarBytes[AvatarOffset.BALLOON_SPAWN_TRIGGER_Y_OFFSET] = data.Dungeons[data.Dungeons.Count - 2].Y; avatarBytes[AvatarOffset.LBC_DUNGEON_EXIT_X_OFFSET] = data.Dungeons[data.Dungeons.Count - 2].X; avatarBytes[AvatarOffset.LBC_DUNGEON_EXIT_Y_OFFSET] = data.Dungeons[data.Dungeons.Count - 2].Y; avatarBytes[AvatarOffset.BALLOON_SPAWN_LOCATION_X_OFFSET] = data.BalloonSpawn.X; avatarBytes[AvatarOffset.BALLOON_SPAWN_LOCATION_Y_OFFSET] = data.BalloonSpawn.Y; var avatarBytesList = new List <byte>(avatarBytes); for (int i = 0; i < OriginalShrineText.Count; i++) { if (data.ShrineText[i].Length > OriginalShrineText[i].Length) { throw new Exception($"Shrine text \"{data.ShrineText[i]}\" is too long."); } data.ShrineText[i] = data.ShrineText[i].PadRight(OriginalShrineText[i].Length, ' '); avatarBytesList.RemoveRange(OriginalShrineTextStartOffset[i], OriginalShrineText[i].Length); avatarBytesList.InsertRange(OriginalShrineTextStartOffset[i], Encoding.ASCII.GetBytes(data.ShrineText[i])); } for (int i = 0; i < OriginalLBText.Count; i++) { if (data.LBText[i].Length > OriginalLBText[i].Length) { throw new Exception($"LB text \"{data.LBText[i]}\" is too long."); } data.LBText[i] = data.LBText[i].PadRight(OriginalLBText[i].Length, ' '); avatarBytesList.RemoveRange(OriginalLBTextStartOffset[i], OriginalLBText[i].Length); avatarBytesList.InsertRange(OriginalLBTextStartOffset[i], Encoding.ASCII.GetBytes(data.LBText[i])); } avatarBytes = avatarBytesList.ToArray(); for (int i = 0; i < OriginalLBHelpText.Count; i++) { if (data.LBHelpText[i].Length > OriginalLBHelpText[i].Length) { throw new Exception($"LB text \"{data.LBHelpText[i]}\" is too long."); } data.LBHelpText[i] = data.LBHelpText[i].PadRight(OriginalLBHelpText[i].Length, ' '); avatarBytesList.RemoveRange(OriginalLBHelpTextStartOffset[i], OriginalLBHelpText[i].Length); avatarBytesList.InsertRange(OriginalLBHelpTextStartOffset[i], Encoding.ASCII.GetBytes(data.LBHelpText[i])); } avatarBytes = avatarBytesList.ToArray(); var currentMantraOffset = 0; var mantraSize = 0; for (int i = 0; i < data.Mantras.Count; i++) { avatarBytes[AvatarOffset.MANTRA_POINTERS_OFFSET + i * 2] = (byte)(avatarBytes[AvatarOffset.MANTRA_POINTERS_OFFSET] + mantraSize); mantraSize += data.Mantras[i].Length + 1; var textBytes = Encoding.ASCII.GetBytes(data.Mantras[i]); for (int j = 0; j < textBytes.Length; j++) { avatarBytes[AvatarOffset.MANTRA_OFFSET + currentMantraOffset] = textBytes[j]; currentMantraOffset++; } avatarBytes[AvatarOffset.MANTRA_OFFSET + currentMantraOffset] = 0x00; currentMantraOffset++; if (currentMantraOffset > MantraMaxSize) { throw new Exception($"Mantra text is too long."); } } var wordOfPassageBytes = Encoding.ASCII.GetBytes(data.WordOfPassage.ToLower()); for (int j = 0; j < wordOfPassageBytes.Length; j++) { avatarBytes[AvatarOffset.WORD_OF_PASSAGE + j] = wordOfPassageBytes[j]; } avatarBytes[AvatarOffset.DEMON_SPAWN_TRIGGER_X1_OFFSET] = data.DaemonSpawnX1; avatarBytes[AvatarOffset.DEMON_SPAWN_TRIGGER_X2_OFFSET] = data.DaemonSpawnX2; avatarBytes[AvatarOffset.DEMON_SPAWN_TRIGGER_Y1_OFFSET] = data.DaemonSpawnY1; avatarBytes[AvatarOffset.DEMON_SPAWN_TRIGGER_Y2_OFFSET] = data.DaemonSpawnY2; avatarBytes[AvatarOffset.DEMON_SPAWN_LOCATION_X_OFFSET] = data.DaemonSpawnLocationX; for (int i = 0; i < data.PirateCove.Count; i++) { avatarBytes[AvatarOffset.PIRATE_COVE_X_OFFSET + i] = data.PirateCove[i].X; avatarBytes[AvatarOffset.PIRATE_COVE_Y_OFFSET + i] = data.PirateCove[i].Y; } avatarBytes[AvatarOffset.PIRATE_COVE_SPAWN_TRIGGER_X_OFFSET1] = data.PirateCoveSpawnTrigger.X; avatarBytes[AvatarOffset.PIRATE_COVE_SPAWN_TRIGGER_Y_OFFSET1] = data.PirateCoveSpawnTrigger.Y; avatarBytes[AvatarOffset.PIRATE_COVE_SPAWN_TRIGGER_X_OFFSET2] = data.PirateCoveSpawnTrigger.X; avatarBytes[AvatarOffset.PIRATE_COVE_SPAWN_TRIGGER_Y_OFFSET2] = data.PirateCoveSpawnTrigger.Y; avatarBytes[AvatarOffset.WHIRLPOOL_EXIT_X_OFFSET] = data.WhirlpoolExit.X; avatarBytes[AvatarOffset.WHIRLPOOL_EXIT_Y_OFFSET] = data.WhirlpoolExit.Y; for (int i = 0; i < data.SpellsRecipes.Count; i++) { avatarBytes[AvatarOffset.SPELL_RECIPE_OFFSET + i] = data.SpellsRecipes[i].Byte; } // Cast exclusion isn't precise enough so allow them to cast anywhere and exclude the destination avatarBytes[AvatarOffset.BLINK_CAST_EXCLUSION_X1_OFFSET] = data.BlinkCastExclusionX1; avatarBytes[AvatarOffset.BLINK_CAST_EXCLUSION_X2_OFFSET] = data.BlinkCastExclusionX2; avatarBytes[AvatarOffset.BLINK_CAST_EXCLUSION_Y1_OFFSET] = data.BlinkCastExclusionY1; avatarBytes[AvatarOffset.BLINK_CAST_EXCLUSION_Y2_OFFSET] = data.BlinkCastExclusionY2; avatarBytes[AvatarOffset.BLINK_DESTINATION_EXCLUSION_X1_OFFSET] = data.BlinkDestinationExclusionX1; avatarBytes[AvatarOffset.BLINK_DESTINATION_EXCLUSION_X2_OFFSET] = data.BlinkDestinationExclusionX2; avatarBytes[AvatarOffset.BLINK_DESTINATION_EXCLUSION_Y1_OFFSET] = data.BlinkDestinationExclusionY1; avatarBytes[AvatarOffset.BLINK_DESTINATION_EXCLUSION_Y2_OFFSET] = data.BlinkDestinationExclusionY2; avatarBytes[AvatarOffset.BLINK_DESTINATION2_EXCLUSION_X1_OFFSET] = data.BlinkDestinationExclusion2X1; avatarBytes[AvatarOffset.BLINK_DESTINATION2_EXCLUSION_X2_OFFSET] = data.BlinkDestinationExclusion2X2; avatarBytes[AvatarOffset.BLINK_DESTINATION2_EXCLUSION_Y1_OFFSET] = data.BlinkDestinationExclusion2Y1; avatarBytes[AvatarOffset.BLINK_DESTINATION2_EXCLUSION_Y2_OFFSET] = data.BlinkDestinationExclusion2Y2; avatarBytes[AvatarOffset.ENABLE_MIX_QUANTITY_OFFSET] = flags.MixQuantity ? (byte)0x0 : (byte)0x9; if (flags.MixQuantity) { SpoilerLog.Add(SpoilerCategory.Feature, $"Mix Quantity Enabled"); } avatarBytes[AvatarOffset.ENABLE_SLEEP_BACKOFF_OFFSET] = flags.SleepLockAssist ? (byte)0x0 : (byte)0x9; if (flags.SleepLockAssist) { SpoilerLog.Add(SpoilerCategory.Feature, $"Sleep Lock Assist Enabled"); } avatarBytes[AvatarOffset.ENABLE_ACTIVE_PLAYER_1_OFFSET] = flags.ActivePlayer ? (byte)0x0 : (byte)0x9; if (flags.ActivePlayer) { SpoilerLog.Add(SpoilerCategory.Feature, $"Active Player Enabled"); } avatarBytes[AvatarOffset.ENABLE_HIT_CHANCE_OFFSET] = flags.HitChance ? (byte)0x0 : (byte)0x9; if (flags.HitChance) { SpoilerLog.Add(SpoilerCategory.Feature, $"Apple II Hit Chance Enabled"); } avatarBytes[AvatarOffset.ENABLE_DIAGONAL_ATTACK_OFFSET] = flags.DiagonalAttack ? (byte)0x0 : (byte)0x9; if (flags.DiagonalAttack) { SpoilerLog.Add(SpoilerCategory.Feature, $"Diagonal Attack Enabled"); } avatarBytes[AvatarOffset.ENABLE_SACRIFICE_FIX_OFFSET] = flags.SacrificeFix ? (byte)0x0 : (byte)0x9; if (flags.SacrificeFix) { SpoilerLog.Add(SpoilerCategory.Fix, $"Sacrifice Fix Enabled"); } for (int i = 0; i < data.AbyssEjectionLocations.Count; i++) { //Console.WriteLine(Talk.GetSextantText(data.AbyssEjectionLocations[i])); avatarBytes[AvatarOffset.ABYSS_EJECTION_LOCATIONS_X + i] = data.AbyssEjectionLocations[i].X; avatarBytes[AvatarOffset.ABYSS_EJECTION_LOCATIONS_Y + i] = data.AbyssEjectionLocations[i].Y; } for (int townIdx = 0; townIdx < 16; townIdx++) { for (int shopIdx = 0; shopIdx < 8; shopIdx++) { avatarBytes[townIdx * 8 + shopIdx + AvatarOffset.SHOP_LOCATION_OFFSET] = data.ShopLocations[townIdx][shopIdx]; } } var encodeBytes = Encoding.ASCII.GetBytes(flags.GetEncoded()); for (int encodeIdx = 0; encodeIdx < encodeBytes.Length; encodeIdx++) { avatarBytes[AvatarOffset.ENCODED_FLAGS_OFFSET + encodeIdx] = encodeBytes[encodeIdx]; } var seedBytes = Encoding.ASCII.GetBytes(flags.Seed.ToString()); for (int seedIdx = 0; seedIdx < seedBytes.Length; seedIdx++) { avatarBytes[AvatarOffset.SEED_OFFSET + seedIdx] = seedBytes[seedIdx]; } if (flags.Runes) { for (int i = 0; i < 8; i++) { avatarBytes[AvatarOffset.CITY_RUNE_MASK_PAIRS_OFFSET + i * 2] = data.Items[i + UltimaData.ITEM_RUNE_HONESTY].Location; } } if (flags.MonsterDamage != 2) { avatarBytes[AvatarOffset.MONSTER_DAMAGE_BITSHIFT_OFFSET] = 0xB1; avatarBytes[AvatarOffset.MONSTER_DAMAGE_BITSHIFT_OFFSET + 1] = (byte)flags.MonsterDamage; avatarBytes[AvatarOffset.MONSTER_DAMAGE_BITSHIFT_OFFSET + 2] = 0xD3; } if (flags.WeaponDamage != 2) { var multiplier = 1.0f; switch (flags.WeaponDamage) { case 1: multiplier = 1.5f; break; case 3: multiplier = 0.5f; break; } for (int i = 0; i < 16; i++) { var originalDamage = avatarBytes[AvatarOffset.WEAPON_DAMAGE_OFFSET + i]; var newDamage = avatarBytes[AvatarOffset.WEAPON_DAMAGE_OFFSET + i] * multiplier; avatarBytes[AvatarOffset.WEAPON_DAMAGE_OFFSET + i] = (byte)Math.Max(0x01, Math.Min(0xFF, avatarBytes[AvatarOffset.WEAPON_DAMAGE_OFFSET + i] * multiplier)); } } if (flags.EarlierMonsters) { ushort tierCutover = 1000; var tierCutoverBytes = BitConverter.GetBytes(tierCutover); for (int offset = 0; offset < tierCutoverBytes.Length; offset++) { avatarBytes[AvatarOffset.MONSTER_SPAWN_TIER_ONE + offset] = tierCutoverBytes[offset]; } tierCutover = 2000; tierCutoverBytes = BitConverter.GetBytes(tierCutover); for (int offset = 0; offset < tierCutoverBytes.Length; offset++) { avatarBytes[AvatarOffset.MONSTER_SPAWN_TIER_TWO + offset] = tierCutoverBytes[offset]; } tierCutover = 3000; tierCutoverBytes = BitConverter.GetBytes(tierCutover); for (int offset = 0; offset < tierCutoverBytes.Length; offset++) { avatarBytes[AvatarOffset.MONSTER_SPAWN_TIER_THREE + offset] = tierCutoverBytes[offset]; } } if (flags.MonsterQty) { var cmd = new byte[] { 0xB8, 0x10, 0x00, 0x90, 0x90 }; for (int i = 0; i < cmd.Length; i++) { avatarBytes[AvatarOffset.MONSTER_QTY_ONE + i] = cmd[i]; } cmd = new byte[] { 0x3D, 0x08, 0x00, 0x7D, 0xE4, 0x90 }; for (int i = 0; i < cmd.Length; i++) { avatarBytes[AvatarOffset.MONSTER_QTY_TWO + i] = cmd[i]; } } if (flags.NoRequireFullParty) { avatarBytes[AvatarOffset.ABYSS_PARTY_COMPARISON] = 0x76; avatarBytes[AvatarOffset.LB_PARTY_COMPARISON] = 0x00; } if (flags.VGAPatch) { ApplyVGAPatch(); } }