//private static Dictionary<string, int> dungeonIdx = new Dictionary<string, int>() //{ // { "deceit", 0 }, // { "despise", 1 }, // { "destard", 2 }, // { "wrong", 3 }, // { "covetous", 4 }, // { "shame", 5 }, // { "hythloth", 6 }, // { "abyss", 7 } //}; public void Load(string path, UltimaData data, Flags flags) { var files = Directory.GetFiles(path, "*.DNG"); foreach (var file in files) { if (!file.Contains("CAMP.DNG")) { FileHelper.TryBackupOriginalFile(file, false); if (flags.FixHythloth && file.Contains("HYTHLOTH")) { using (var deltaStream = new MemoryStream(Patches.HYTHLOTH)) { Dungeon dungeon = new Dungeon(deltaStream, data); dungeons.Add(Path.GetFileNameWithoutExtension(file), dungeon); } SpoilerLog.Add(SpoilerCategory.Fix, "Hythloth lvl 6 fixed."); } else { using (var dngStream = new System.IO.FileStream($"{file}.orig", System.IO.FileMode.Open)) { Dungeon dungeon = new Dungeon(dngStream, data); dungeons.Add(Path.GetFileNameWithoutExtension(file), dungeon); } } } } }
public override void Randomize(UltimaData ultimaData, Random random1, Random random2) { SpoilerLog.Add(SpoilerCategory.Location, "Locations unchanged"); // Used to output abyss shape //int xSize = 32 + 10 - 2; //int ySize = 64 + 17 - 1; //int xOffset = 256 - xSize - 2; //int yOffset = 256 - ySize - 1; //using (var worldFile = new System.IO.BinaryWriter(new System.IO.FileStream("abyssnew", System.IO.FileMode.OpenOrCreate))) //{ // Console.Write(xSize); // Console.Write(ySize); // for (int y = 0; y < ySize; y++) // { // for (int x = 0; x < xSize; x++) // { // _worldMapTiles[x, y] = _worldMapTiles[x + xOffset, y + yOffset]; // worldFile.Write(_worldMapTiles[x, y]); // Console.Write(_worldMapTiles[x, y]); // } // Console.WriteLine(); // } //} return; }
public Dungeon(Stream dngStream, UltimaData data) { BinaryReader readBinary = new BinaryReader(dngStream); // Load each level for (int l = 0; l < 8; l++) { byte[] levelArr = new byte[8 * 8]; readBinary.Read(levelArr, 0, 8 * 8); for (int i = 0; i < levelArr.Length; i++) { map[l, i / 8, i % 8] = levelArr[i]; } } // Load each room byte[] roomArr = new byte[256]; while (readBinary.Read(roomArr, 0, 256) == 256) { byte[] roomClone = new byte[256]; roomArr.CopyTo(roomClone, 0); rooms.Add(roomClone); } }
public void Update(UltimaData ultimaData, Flags flags) { if (flags.Fixes) { towns["SERPENT"].npcConversationIdx[28] = 02; towns["SERPENT"].npcConversationIdx[29] = 02; SpoilerLog.Add(SpoilerCategory.Fix, $"Serpent's Hold gate guard fix"); } }
//public Dictionary<string, string> ReadHashes() //{ // var file = Path.Combine("hashes", "title_hash.json"); // var hashJson = System.IO.File.ReadAllText(file); // var hashes = JsonConvert.DeserializeObject<Dictionary<string, string>>(hashJson); // return hashes; //} //public void WriteHashes(string path) //{ // var file = Path.Combine(path, filename); // var townTalkHash = new Dictionary<string, string>(); // var hash = HashHelper.GetHashSha256(file); // Console.WriteLine($"{file}: {HashHelper.BytesToString(hash)}"); // townTalkHash.Add(Path.GetFileName(file), HashHelper.BytesToString(hash)); // string json = JsonConvert.SerializeObject(townTalkHash); // the dictionary is inside client object // //write string to file // System.IO.File.WriteAllText(@"party_hash.json", json); //} public void Update(UltimaData data) { for (int i = 0; i < data.StartingCharacters.Count; i++) { var character = data.StartingCharacters[i]; partyBytes.OverwriteBytes(character.Hp, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_HP_OFFSET); partyBytes.OverwriteBytes(character.MaxHp, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_MAX_HP_OFFSET); partyBytes.OverwriteBytes(character.XP, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_XP_OFFSET); partyBytes.OverwriteBytes(character.Str, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_STR_OFFSET); partyBytes.OverwriteBytes(character.Dex, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_DEX_OFFSET); partyBytes.OverwriteBytes(character.Int, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_INT_OFFSET); partyBytes.OverwriteBytes(character.Mp, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_MP_OFFSET); partyBytes.OverwriteBytes(character.Weapon, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_WEAPON_OFFSET); partyBytes.OverwriteBytes(character.Armor, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_ARMOR_OFFSET); var textBytes = Encoding.ASCII.GetBytes(character.Name); if (textBytes.Length > 16) { throw new Exception($"Part name {character.Name} is too long."); } int j; for (j = 0; j < textBytes.Length; j++) { partyBytes[CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_NAME_OFFSET + j] = textBytes[j]; } partyBytes[CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_NAME_OFFSET + j] = 0x00; partyBytes[CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_SEX_OFFSET] = (character.Sex == 'M' ? (byte)0xb : (byte)0xc); partyBytes[CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_CLASS_OFFSET] = character.Class; partyBytes[CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_STATUS_OFFSET] = (byte)character.Status; } var foodBytes = BitConverter.GetBytes(data.StartingFood); for (int offset = 0; offset < foodBytes.Length; offset++) { partyBytes[FOOD_OFFSET + offset] = foodBytes[offset]; } partyBytes.OverwriteBytes(data.StartingGold, GOLD_OFFSET); partyBytes.OverwriteBytes(data.StartingItems, ITEMS1_OFFSET); partyBytes.OverwriteBytes(data.StartingEquipment, EQUIPMENT_OFFSET); partyBytes.OverwriteBytes(data.StartingArmor, ARMOR_OFFSET); partyBytes.OverwriteBytes(data.StartingWeapons, WEAPONS_OFFSET); partyBytes.OverwriteBytes(data.StartingReagents, REAGENTS_OFFSET); partyBytes.OverwriteBytes(data.StartingMixtures, MIXTURES_OFFSET); partyBytes[STONES_OFFSET] = data.StartingStones; partyBytes[RUNES_OFFSET] = data.StartingRunes; }
public void Load(string path, UltimaData data) { var files = Directory.GetFiles(path, "*.ULT"); foreach (var file in files) { FileHelper.TryBackupOriginalFile(file, false); using (var twnStream = new System.IO.FileStream($"{file}.orig", System.IO.FileMode.Open)) { Town town = new Town(twnStream, data); towns.Add(Path.GetFileNameWithoutExtension(file), town); } } }
//public Dictionary<string, string> ReadHashes() //{ // var file = Path.Combine("hashes", "title_hash.json"); // var hashJson = System.IO.File.ReadAllText(file); // var hashes = JsonConvert.DeserializeObject<Dictionary<string, string>>(hashJson); // return hashes; //} //public void WriteHashes(string path) //{ // var file = Path.Combine(path, filename); // var townTalkHash = new Dictionary<string, string>(); // var hash = HashHelper.GetHashSha256(file); // Console.WriteLine($"{file}: {HashHelper.BytesToString(hash)}"); // townTalkHash.Add(Path.GetFileName(file), HashHelper.BytesToString(hash)); // string json = JsonConvert.SerializeObject(townTalkHash); // the dictionary is inside client object // //write string to file // System.IO.File.WriteAllText(@"title_hash.json", json); //} public void Update(UltimaData data, Flags flags, string encode) { for (int offset = 0; offset < 8; offset++) { titleBytes[START_X_OFFSET + offset] = data.StartingPositions[offset].X; titleBytes[START_Y_OFFSET + offset] = data.StartingPositions[offset].Y; } titleBytes[ENABLE_KARMA_OVERRIDE_OFFSET] = flags.KarmaSetPercentage > 0 ? (byte)0x0 : (byte)0x9; for (int offset = 0; offset < 8; offset++) { titleBytes[KARMA_OVERRIDE_VALUES_OFFSET + offset] = (data.StartingKarma[offset] == 100 ? (byte)0 : data.StartingKarma[offset]); } var encodeBytes = Encoding.ASCII.GetBytes(encode); for (int i = 0; i < encodeBytes.Length; i++) { titleBytes[FLAG_ENCODE_OFFSET + i] = encodeBytes[i]; } }
public Town(FileStream twnStream, UltimaData data) { BinaryReader readBinary = new BinaryReader(twnStream); byte[] mapArr = new byte[32 * 32]; readBinary.Read(mapArr, 0, 32 * 32); for (int i = 0; i < 32 * 32; i++) { map[i / 32, i % 32] = mapArr[i]; } readBinary.Read(npcTileIdx, 0, 32); readBinary.Read(npcStartX, 0, 32); readBinary.Read(npcStartY, 0, 32); //Unused entries readBinary.Read(npcOldX, 0, 32); readBinary.Read(npcOldY, 0, 32); readBinary.Read(npcAggressivity, 0, 32); readBinary.Read(npcMovement, 0, 32); readBinary.Read(npcConversationIdx, 0, 32); }
public Image ToClothMap(UltimaData data, Random random) { using (Image <Rgba32> deep_water = Image.Load <Rgba32>("E:\\Projects\\U4DosRandomizer\\Assets\\deep_water.png")) { using (Image <Rgba32> grass = Image.Load <Rgba32>("E:\\Projects\\U4DosRandomizer\\Assets\\grass.png")) { var image = new Image <Rgba32>(WorldMapGenerateMap.SIZE * 4, WorldMapGenerateMap.SIZE * 4); for (int y = 0; y < WorldMapGenerateMap.SIZE * 4; y++) { Span <Rgba32> deepWaterRowSpan = deep_water.GetPixelRowSpan(y); Span <Rgba32> grassRowSpan = grass.GetPixelRowSpan(y); Span <Rgba32> pixelRowSpan = image.GetPixelRowSpan(y); for (int x = 0; x < WorldMapGenerateMap.SIZE * 4; x++) { //if (colorMap.ContainsKey(_worldMapTiles[x, y])) //{ // pixelRowSpan[x] = colorMap[_worldMapTiles[x, y]]; //} //else //{ // pixelRowSpan[x] = SixLabors.ImageSharp.Color.White; //} if (_worldMapTiles[x / 4, y / 4] == TileInfo.Deep_Water) { pixelRowSpan[x] = deepWaterRowSpan[x]; } else { pixelRowSpan[x] = grassRowSpan[x]; } } } return(image); } } }
public Region FindNearestRegion(ICoordinate targetTile, UltimaData data, out IList <ITile> outPath) { outPath = null; return(null); }
public abstract void Randomize(UltimaData ultimaData, Random random1, Random random2);
public abstract void Load(string path, int v, int mapGeneratorSeed, int otherRandomSeed, UltimaData ultimaData);
public override void Randomize(UltimaData ultimaData, Random random1, Random random2) { var swapPool = new List <Tuple <TileDirtyWrapper, ICoordinate, int> >(); for (int i = 0; i < ultimaData.Castles.Count; i++) { //SpoilerLog.Add(SpoilerCategory.Location, $"{ultimaData.Castles[i].Y:X2}"); swapPool.Add(new Tuple <TileDirtyWrapper, ICoordinate, int>(ultimaData.Castles[i].Copy(this), ultimaData.AbyssEjectionLocations[8 + i], ultimaData.LOC_LYCAEUM + i)); } for (int i = 0; i < ultimaData.Towns.Count; i++) { //SpoilerLog.Add(SpoilerCategory.Location, $"{ultimaData.Towns[i].Y:X2}"); if (i != 9 && i != 11) { ICoordinate startingPosition = null; if (i < 8) { startingPosition = ultimaData.StartingPositions[i]; } else { // No starting positions for villages. Make one. var validPositions = GetPathableTilesNear(ultimaData.Towns[i], 3, IsWalkable); startingPosition = validPositions[random1.Next(0, validPositions.Count)]; } swapPool.Add(new Tuple <TileDirtyWrapper, ICoordinate, int>(ultimaData.Towns[i].Copy(this), startingPosition, ultimaData.LOC_TOWNS + i)); } } //for (int i = 0; i < swapPool.Count; i++) //{ // SpoilerLog.Add(SpoilerCategory.Location, $"{swapPool[i].Item1.Y:X2}"); //} for (int i = 0; i < swapPool.Count; i++) { var val = random1.Next(0, (swapPool.Count - 1) - i); Swap(swapPool, val, (swapPool.Count - 1) - i); } for (int i = 0; i < swapPool.Count; i++) { if (i < ultimaData.Castles.Count) { ultimaData.Castles[i].X = swapPool[i].Item1.X; ultimaData.Castles[i].Y = swapPool[i].Item1.Y; ultimaData.Castles[i].SetTile(0x0B); ultimaData.AbyssEjectionLocations[8 + i].X = swapPool[i].Item2.X; ultimaData.AbyssEjectionLocations[8 + i].Y = swapPool[i].Item2.Y; SpoilerLog.Add(SpoilerCategory.Location, $"{ultimaData.LocationNames[i + ultimaData.LOC_LYCAEUM - 1]} moved to {ultimaData.LocationNames[swapPool[i].Item3-1]}"); } else { int townIdx = i - 3; if (i >= 12) { townIdx++; } if (i >= 13) { townIdx++; } byte tile = (byte)(townIdx > 8 ? 0x0C : 0x0A); if (townIdx == 7) { tile = 0x1D; } ultimaData.Towns[townIdx].X = swapPool[i].Item1.X; ultimaData.Towns[townIdx].Y = swapPool[i].Item1.Y; ultimaData.Towns[townIdx].SetTile(tile); if (townIdx < 8) { ultimaData.AbyssEjectionLocations[i].X = swapPool[i].Item2.X; ultimaData.AbyssEjectionLocations[i].Y = swapPool[i].Item2.Y; ultimaData.StartingPositions[townIdx].X = swapPool[i].Item2.X; ultimaData.StartingPositions[townIdx].Y = swapPool[i].Item2.Y; } SpoilerLog.Add(SpoilerCategory.Location, $"{ultimaData.LocationNames[townIdx + ultimaData.LOC_TOWNS - 1]} moved to {ultimaData.LocationNames[swapPool[i].Item3-1]}"); } } // Shrines // Don't move humility var swapValues = new List <int>(); for (int i = 0; i < ultimaData.Shrines.Count; i++) { swapValues.Add(i); } for (int i = 0; i < ultimaData.Shrines.Count - 1; i++) { var val = random1.Next(0, (ultimaData.Shrines.Count - 2) - i); Swap(ultimaData.Shrines, val, (ultimaData.Shrines.Count - 2) - i); Swap(swapValues, val, (ultimaData.Shrines.Count - 2) - i); } for (int i = 0; i < ultimaData.Shrines.Count; i++) { SpoilerLog.Add(SpoilerCategory.Location, $"{ultimaData.LocationNames[i + ultimaData.LOC_SHRINES - 1]} shrine moved to {ultimaData.LocationNames[swapValues[i] + ultimaData.LOC_SHRINES - 1]}"); } // Moongates swapValues = new List <int>(); var originalNewMoonX = ultimaData.Moongates[0].X; var originalNewMoonY = ultimaData.Moongates[0].Y; for (int i = 0; i < ultimaData.Moongates.Count; i++) { swapValues.Add(i); } for (int i = 0; i < ultimaData.Moongates.Count; i++) { var val = random1.Next(0, (ultimaData.Moongates.Count - 1) - i); Swap(ultimaData.Moongates, val, (ultimaData.Moongates.Count - 1) - i); Swap(swapValues, val, (ultimaData.Moongates.Count - 1) - i); } for (int i = 0; i < ultimaData.Shrines.Count; i++) { SpoilerLog.Add(SpoilerCategory.Location, $"{ultimaData.LocationNames[i + ultimaData.LOC_TOWNS - 1]} moongate moved to {ultimaData.LocationNames[swapValues[i] + ultimaData.LOC_TOWNS - 1]}"); } // Dungeons // Don't move Abyss swapValues = new List <int>(); for (int i = 0; i < ultimaData.Dungeons.Count; i++) { swapValues.Add(i); } for (int i = 0; i < ultimaData.Dungeons.Count - 1; i++) { var val = random1.Next(0, (ultimaData.Dungeons.Count - 2) - i); Swap(ultimaData.Dungeons, val, (ultimaData.Dungeons.Count - 2) - i); Swap(swapValues, val, (ultimaData.Dungeons.Count - 2) - i); } for (int i = 0; i < ultimaData.Dungeons.Count; i++) { SpoilerLog.Add(SpoilerCategory.Location, $"{ultimaData.LocationNames[i + ultimaData.LOC_DUNGEONS - 1]} moved to {ultimaData.LocationNames[swapValues[i] + ultimaData.LOC_DUNGEONS - 1]}"); } // Items swapValues = new List <int>(); for (int i = 0; i < 10; i++) { swapValues.Add(i); } for (int i = 0; i < 10; i++) { var val = random1.Next(0, (10 - 1) - i); Swap(ultimaData.Items, val, (10 - 1) - i); Swap(swapValues, val, (10 - 1) - i); } for (int i = 0; i < 10; i++) { SpoilerLog.Add(SpoilerCategory.Location, $"{ultimaData.ItemNames[i]} moved to {ultimaData.ItemNames[swapValues[i]]}"); } //Whatever is now at the original new moon moongate location gets moved to whereever the new moongate is now var item = ultimaData.Items.Single(x => x.X == originalNewMoonX && x.Y == originalNewMoonY && x.Location == 0x00); item.X = ultimaData.Moongates[0].X; item.Y = ultimaData.Moongates[0].Y; return; }
public override void Load(string path, int mapSeed, int mapGeneratorSeed, int otherRandomSeed, UltimaData ultimaData) { var file = Path.Combine(path, filename); FileHelper.TryBackupOriginalFile(file); _worldMapTiles = new byte[SIZE, SIZE]; int chunkwidth = 32; int chunkSize = chunkwidth * chunkwidth; byte[] chunk; // = new byte[chunkSize]; using (var worldMap = new System.IO.BinaryReader(new System.IO.FileStream($"{file}.orig", System.IO.FileMode.Open))) { for (int chunkCount = 0; chunkCount < 64; chunkCount++) { chunk = worldMap.ReadBytes(chunkSize); // Copy the chunk over for (int i = 0; i < chunkSize; i++) { _worldMapTiles[i % chunkwidth + chunkCount % 8 * chunkwidth, i / chunkwidth + chunkCount / 8 * chunkwidth] = chunk[i]; } } } //RemoveAvatarIsle(); }
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(); } }
public void Update(UltimaData ultimaData, Avatar avatar, Flags flags, IWorldMap worldMap) { if (flags.Overworld == 5) { Person person = null; // --- Items --- if (ultimaData.Items[ultimaData.ITEM_BELL].Changed) { person = FindPerson("Garam"); person.KeywordResponse2 = ReplaceSextantText(person.KeywordResponse2, GetSextantText(ultimaData.Items[ultimaData.ITEM_BELL])); } if (ultimaData.Items[ultimaData.ITEM_SKULL].Changed) { person = FindPerson("Jude"); person.Yes = ReplaceSextantText(person.Yes, GetSextantText(ultimaData.Items[ultimaData.ITEM_SKULL])); } if (ultimaData.Items[ultimaData.ITEM_NIGHTSHADE].Changed) { person = FindPerson("Virgil"); person.KeywordResponse2 = ReplaceSextantText(person.KeywordResponse2, GetSextantText(ultimaData.Items[ultimaData.ITEM_NIGHTSHADE])); } if (ultimaData.Towns[ultimaData.LOC_MAGINCIA - ultimaData.LOC_TOWNS].IsDirty()) { person = FindPerson("Shawn"); person.No = ReplaceSextantText(person.No, GetSextantText(ultimaData.Towns[ultimaData.LOC_MAGINCIA - ultimaData.LOC_TOWNS])); } // Mandrake if (ultimaData.Items[ultimaData.ITEM_MANDRAKE].Changed || ultimaData.Items[ultimaData.ITEM_MANDRAKE2].Changed) { person = FindPerson("Calumny"); if (!flags.ClothMap) { person.KeywordResponse2 = $"Mandrake is found near {GetSextantText(ultimaData.Items[ultimaData.ITEM_MANDRAKE])}\nand\n{GetSextantText(ultimaData.Items[ultimaData.ITEM_MANDRAKE2])} "; } else { var mandrakeTile = worldMap.GetCoordinate(ultimaData.Items[ultimaData.ITEM_MANDRAKE].X, ultimaData.Items[ultimaData.ITEM_MANDRAKE].Y); IList <ITile> path = null; var mandrakeRegion = worldMap.FindNearestRegion(mandrakeTile, ultimaData, out path); var mandrakeText = "Mandrake root is found only "; if (mandrakeRegion != null && mandrakeRegion.Tiles.Any(c => c.Equals(mandrakeTile))) { mandrakeText += $"in the {mandrakeRegion.Name} "; } else if (path != null && path.Count < 11) { mandrakeText += $"near the {mandrakeRegion.Name} "; } else { mandrakeText += $"near {GetSextantText(ultimaData.Items[ultimaData.ITEM_MANDRAKE])} "; } mandrakeTile = worldMap.GetCoordinate(ultimaData.Items[ultimaData.ITEM_MANDRAKE2].X, ultimaData.Items[ultimaData.ITEM_MANDRAKE2].Y); IList <ITile> path2 = null; var mandrakeRegion2 = worldMap.FindNearestRegion(mandrakeTile, ultimaData, out path2); if (mandrakeRegion == mandrakeRegion2 && path.Count < 11) { path = null; mandrakeRegion = null; } else { mandrakeRegion = mandrakeRegion2; path = path2; if (mandrakeRegion != null && mandrakeRegion.Tiles.Any(c => c == mandrakeTile)) { mandrakeText += $"and in the {mandrakeRegion.Name} "; } else if (path != null && path.Count < 11) { mandrakeText += $"and near the {mandrakeRegion.Name} "; } else { mandrakeText += $"and near {GetSextantText(ultimaData.Items[ultimaData.ITEM_MANDRAKE2])} "; } } mandrakeText += $"where the ground is always damp."; person.KeywordResponse2 = mandrakeText; } } // Horn // TODO make response descriptive if (ultimaData.Items[ultimaData.ITEM_HORN].Changed) { person = FindPerson("Malchor"); person.KeywordResponse2 = $"Some say that\nthe silver horn\nis buried at\n{GetSextantText(ultimaData.Items[ultimaData.ITEM_HORN])}"; } // Wheel // TODO make response descriptive if (ultimaData.Items[ultimaData.ITEM_WHEEL].Changed) { person = FindPerson("Lassorn"); person.KeywordResponse2 = $"She went down in\nthe deep waters\nat\n{GetSextantText(ultimaData.Items[ultimaData.ITEM_WHEEL])}!"; } // TODO Black stone currently at the moongate will need to change this text if we ever do randomize it person = FindPerson("Merlin"); // White stone // TODO make response descriptive if (ultimaData.Items[ultimaData.ITEM_WHITE_STONE].Changed) { person = FindPerson("Isaac"); person.KeywordResponse2 = $"The white stone\nsits atop the\nmountains at\n{GetSextantText(ultimaData.Items[ultimaData.ITEM_WHITE_STONE])}.\nIt can only be\nreached by one\nwho floats\nwithin the\nclouds."; ultimaData.ShrineText[6 * 3 + 2] = $"If thou dost seek the White Stone search not under the ground but at {GetSextantText(ultimaData.Items[ultimaData.ITEM_WHITE_STONE]).Replace('\n', ' ')}"; } // TODO Book, candle, runes, mystic armor and mystic weapons I'm leaving along for now. Not randomizing stuff in towns yet. // --- End Items --- // --- Shrines --- // Humility // TODO make response descriptive if (ultimaData.Shrines[ultimaData.LOC_HUMILITY - ultimaData.LOC_SHRINES].IsDirty()) { person = FindPerson("Simple"); person.KeywordResponse2 = $"The shrine lies\nnear\n{GetSextantText(ultimaData.Shrines[ultimaData.LOC_HUMILITY - ultimaData.LOC_SHRINES])} and\nis guarded by\nendless hoards\nof daemons!"; person = FindPerson("Wierdrum"); person.KeywordResponse2 = $"Yes, I have been\nto the shrine,\nit lies near\n{GetSextantText(ultimaData.Shrines[ultimaData.LOC_HUMILITY - ultimaData.LOC_SHRINES])}!"; } // Compassion // TODO make response descriptive if (ultimaData.Shrines[ultimaData.LOC_COMPASSION - ultimaData.LOC_SHRINES].IsDirty()) { person = FindPerson("Shapero"); person.Yes = $"Find the shrine\nof compassion\nat\n{GetSextantText(ultimaData.Shrines[ultimaData.LOC_COMPASSION - ultimaData.LOC_SHRINES])}!"; } // Sacrifice // TODO make response descriptive if (ultimaData.Shrines[ultimaData.LOC_SACRIFICE - ultimaData.LOC_SHRINES].IsDirty()) { person = FindPerson("Merida"); person.No = $"The shrine is at\n{GetSextantText(ultimaData.Shrines[ultimaData.LOC_SACRIFICE - ultimaData.LOC_SHRINES])}!"; } // Justice // TODO make response descriptive if (ultimaData.Shrines[ultimaData.LOC_JUSTICE - ultimaData.LOC_SHRINES].IsDirty()) { person = FindPerson("Druid"); person.KeywordResponse2 = $"The shrine is at\n{GetSextantText(ultimaData.Shrines[ultimaData.LOC_JUSTICE - ultimaData.LOC_SHRINES])}!"; } // Honesty // TODO make response descriptive if (ultimaData.Shrines[ultimaData.LOC_HONESTY - ultimaData.LOC_SHRINES].IsDirty()) { person = FindPerson("Calabrini"); person.No = $"Perhaps, the\nshrine which\nlies at\n{GetSextantText(ultimaData.Shrines[ultimaData.LOC_HONESTY - ultimaData.LOC_SHRINES])}!"; } // Honor // TODO make response descriptive if (ultimaData.Shrines[ultimaData.LOC_HONOR - ultimaData.LOC_SHRINES].IsDirty()) { person = FindPerson("Dergin"); person.No = $"The shrine lies at\n{GetSextantText(ultimaData.Shrines[ultimaData.LOC_HONOR - ultimaData.LOC_SHRINES])}!"; } // TODO Spirituality - Do I move this one? person = FindPerson("the Ankh of\nSpirituality"); // Valor // No on gives the directions to Valor so I grabbed his reponse that talked about the shrine and usurped it // TODO make response descriptive if (ultimaData.Shrines[ultimaData.LOC_VALOR - ultimaData.LOC_SHRINES].IsDirty()) { person = FindPerson("Sir Hrothgar"); person.No = $"Thou should seek\nthe shrine of\nvalor at\n{GetSextantText(ultimaData.Shrines[ultimaData.LOC_VALOR - ultimaData.LOC_SHRINES])}!"; } // --- End Shrines --- // --- Runes --- if (flags.Runes) { for (int i = 0; i < 8; i++) { var itemOption = ultimaData.ItemOptions[UltimaData.ITEM_RUNE_HONESTY + i]; foreach (var newPerson in itemOption.People) { person = FindPerson(newPerson.Name, newPerson.Town); if (newPerson.Health != null) { person.Health = newPerson.Health; } if (newPerson.Job != null) { person.Job = newPerson.Job; } if (newPerson.Keyword1 != null) { person.Keyword1 = newPerson.Keyword1; } if (newPerson.Keyword2 != null) { person.Keyword2 = newPerson.Keyword2; } if (newPerson.Yes != null) { person.Yes = newPerson.Yes; } if (newPerson.No != null) { person.No = newPerson.No; } if (newPerson.Question != null) { person.Question = newPerson.Question; } if (newPerson.KeywordResponse1 != null) { person.KeywordResponse1 = newPerson.KeywordResponse1; } if (newPerson.KeywordResponse2 != null) { person.KeywordResponse2 = newPerson.KeywordResponse2; } } } } // --- End Runes --- if (flags.Mantras) { person = FindPerson("Cromwell"); person.KeywordResponse2 = $"The mantra of the shrine of honesty is {Mantras[0].Text.ToUpper()}."; person = FindPerson("Cricket"); person.KeywordResponse2 = $"The mantra of the shrine of compassion is {Mantras[1].Text.ToUpper()}!"; person = FindPerson("Aesop"); person.KeywordResponse2 = $"The mantra of valor is '{Mantras[2].Text.ToUpper()}'. Use it in the shrine on the next isle!"; person = FindPerson("Silent"); person.Job = $"{Mantras[3].Text}... {Mantras[3].Text}..."; person.Health = $"{Mantras[3].Text}... {Mantras[3].Text}..."; person.Keyword1 = $"{Mantras[3].Text.ToUpper()}..."; person.KeywordResponse1 = $"{Mantras[3].Text}... {Mantras[3].Text}..."; person.Keyword2 = $"{Mantras[3].Text.ToUpper()}"; person.KeywordResponse2 = $"{Mantras[3].Text}... {Mantras[3].Text}..."; person = FindPerson("Singsong"); person.KeywordResponse2 = Mantras[4].Limerick; person = FindPerson("Kline"); person.KeywordResponse1 = $"The mantra is '{Mantras[5].Text}'."; person = FindPerson("Barren", "Skara"); person.KeywordResponse1 = $"I know it well, it is '{Mantras[6].Text.ToUpper()}'."; person = FindPerson("the Ankh of\nSpirituality"); person.Keyword2 = Mantras[6].Text.ToUpper(); person = FindPerson("Faultless"); person.KeywordResponse2 = $"The mantra for pride, being the antithesis of humility, is '{new string(Mantras[7].Text.ToString().ToUpper().Reverse().ToArray())}'."; } if (flags.WordOfPassage) { person = FindPerson("Robert Frasier"); person.Yes = $"It is '{ultimaData.WordTruth.ToLower()}'! Seek ye now the other parts!"; person = FindPerson("Lord Robert", "Empath"); person.Yes = $"It is '{ultimaData.WordLove.ToLower()}'! Seek ye now the other parts!"; person = FindPerson("Sentri"); person.KeywordResponse2 = $"I know but one of three syllables - '{ultimaData.WordCourage.ToLower()}'."; } if (flags.RandomizeSpells) { person = FindPerson("Nigel, at thy\nservice."); person.KeywordResponse2 = $"Yes, resurrection it takes: {GetRecipeText(ultimaData.SpellsRecipes['r' - 'a'].Byte)}!"; person = FindPerson("Mentorian"); if (ultimaData.SpellsRecipes['g' - 'a'].Byte == 0xFF) { person.KeywordResponse2 = $"As thou dost bear the ankh I shall tell thee. A gate spell needs { GetRecipeText(ultimaData.SpellsRecipes['g' - 'a'].Byte)}!"; } else { person.KeywordResponse2 = $"Since thou dost bear the ankh I shall tell thee. A gate spell requires { GetRecipeText(ultimaData.SpellsRecipes['g' - 'a'].Byte)}!"; } } // --- Towns and Castles --- // TODO make response descriptive if (ultimaData.Castles[0].IsDirty()) { ultimaData.LBText[3] = $"He says:\nMany truths can\nbe learned at\nthe Lycaeum. It\nlies to the\n{CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Castles[0])}!\n"; } if (ultimaData.Castles[1].IsDirty()) { ultimaData.LBText[4] = $"He says:\nLook for the\nmeaning of Love\nat Empath Abbey.\nThe Abbey sits\n{CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Castles[1])}!\n"; } if (ultimaData.Castles[2].IsDirty()) { ultimaData.LBText[5] = $"\n\nHe says:\nSerpent's Castle\nto the {CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Castles[2])}\nis where\nCourage should\nbe sought!\n"; } if (ultimaData.Towns[ultimaData.LOC_MOONGLOW - ultimaData.LOC_TOWNS].IsDirty()) { ultimaData.LBText[6] = $"\nHe says:\nThe towne\nof Moonglow to\nthe {CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Towns[ultimaData.LOC_MOONGLOW - ultimaData.LOC_TOWNS])} is\nwhere the virtue\nof Honesty\nthrives!\n"; } if (ultimaData.Towns[ultimaData.LOC_BRITAIN - ultimaData.LOC_TOWNS].IsDirty()) { ultimaData.LBText[7] = $"\n\nHe says:\nThe bards in\nBritain to the\n{CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Towns[ultimaData.LOC_BRITAIN - ultimaData.LOC_TOWNS])}\nare well versed\nin\nCompassion!\n"; } if (ultimaData.Towns[ultimaData.LOC_JHELOM - ultimaData.LOC_TOWNS].IsDirty()) { ultimaData.LBText[8] = $"\n\nHe says:\nMany valiant\nfighters come\nfrom Jhelom\nto the \n{CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Towns[ultimaData.LOC_JHELOM - ultimaData.LOC_TOWNS])}!\n"; } if (ultimaData.Towns[ultimaData.LOC_YEW - ultimaData.LOC_TOWNS].IsDirty()) { ultimaData.LBText[9] = $"\n\n\nHe says:\nIn the city of\nYew, to the\n{CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Towns[ultimaData.LOC_YEW - ultimaData.LOC_TOWNS])}, \nJustice is\nserved!\n"; } if (ultimaData.Towns[ultimaData.LOC_MINOC - ultimaData.LOC_TOWNS].IsDirty()) { ultimaData.LBText[10] = $"\nHe says:\nMinoc, towne of\nself-sacrifice,\nlies {CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Towns[ultimaData.LOC_MINOC - ultimaData.LOC_TOWNS])}!\n"; } if (ultimaData.Towns[ultimaData.LOC_TRINSIC - ultimaData.LOC_TOWNS].IsDirty()) { ultimaData.LBText[11] = $"\nHe says:\nThe Paladins who\nstrive for Honor\nare oft seen in\nTrinsic, to the {CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Towns[ultimaData.LOC_TRINSIC - ultimaData.LOC_TOWNS])}!\n"; } if (ultimaData.Towns[ultimaData.LOC_SKARA - ultimaData.LOC_TOWNS].IsDirty()) { ultimaData.LBText[12] = $"\nHe says:\nIn Skara Brae\nthe Spiritual\npath is taught.\nFind it to the\n{CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Towns[ultimaData.LOC_SKARA - ultimaData.LOC_TOWNS])}!\n"; } if (ultimaData.Towns[ultimaData.LOC_MAGINCIA - ultimaData.LOC_TOWNS].IsDirty()) { ultimaData.LBText[13] = $"\n\n\nHe says:\nHumility is the\nfoundation of\nVirtue! The\nruins of proud\nMagincia are a\ntestimony unto\nthe Virtue of\nHumility!\n\nFind the Ruins\nof Magincia to\nthe {CoordinateToCardinal(ultimaData.LCB[0], ultimaData.Towns[ultimaData.LOC_MAGINCIA - ultimaData.LOC_TOWNS])}!\n"; } // --- End Towns and Castles --- // --- Other --- // TODO: Pirate location? Bucaneer's Den? person = FindPerson("Wilmoore"); } else if (flags.Overworld == 2) { var talkToLocation = new Dictionary <Tuple <byte, byte, byte>, Tuple <string, string> >(); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0xB6, 0x36), new Tuple <string, string>("<Item> is found in the Bloody Plains where the ground is always damp.", "<Item> is found in the Bloody Plains where the ground is always damp. Search on the darkest of nights!")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0x64, 0xA5), new Tuple <string, string>("<Item> is found in the Fens of the Dead where the ground is always damp.", "<Item> is found in the Fens of the Dead where the ground is always damp. Search on the darkest of nights!")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0x2E, 0x95), new Tuple <string, string>("<Item> may be found only near lat-J'F\" long-C'O\"!", "<Item> may be found only near lat-J'F\" long-C'O\" only on the darkest of nights!")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0xCD, 0x2C), new Tuple <string, string>("<Item> may be found in the forest outside the shrine in the lake east of the Bloody Plains!", "<Item> may be found in the forest outside the shrine in the lake east of the Bloody Plains only on the darkest of nights!")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0xB0, 0xD0), new Tuple <string, string>("<Item> lies at the bottom of a deep well at sea found at lat-N'A\" long-L'A\".", "<Item> lies at the bottom of a deep well at sea found at lat-N'A\" long-L'A\" but can only be found on the darkest of nights.")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0x2D, 0xAD), new Tuple <string, string>("Some say that <Item> is buried on a small isle off the tip of Spiritwood.", "Some say that <Item> is buried on a small isle off the tip of Spiritwood and can be found when the moons go dark.")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0x60, 0xD7), new Tuple <string, string>("Search the deep waters of the bay in the Cape of Heroes!", "Search the deep waters of the bay in the Cape of Heroes when the moons go dark!")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0xC5, 0xF5), new Tuple <string, string>("It can be found at lat-P'F\" long-M'F\"!", "It can be found at lat-P'F\" long-M'F\" on the darkest night!")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0xE0, 0x85), new Tuple <string, string>("Stand where the gate of both moons dark shall appear.", "Stand where the gate of both moons dark shall appear. Search when the moons go dark!")); talkToLocation.Add(new Tuple <byte, byte, byte>(0x00, 0x40, 0x50), new Tuple <string, string>("<Item> sits atop the Serpent's Spine. It can only be reached by one who floats within the clouds.", "<Item> sits atop the Serpent's Spine. It can only be reached by one who floats within the clouds and when the moons go dark.")); var item = ultimaData.Items[ultimaData.ITEM_BELL]; var talkString = talkToLocation[new Tuple <byte, byte, byte>(item.Location, item.X, item.Y)].Item1; talkString = talkString.Replace("<Item>", "the bell of courage").CapitalizeFirstLetter(); var person = FindPerson("Garam"); person.KeywordResponse2 = talkString; item = ultimaData.Items[ultimaData.ITEM_SKULL]; talkString = talkToLocation[new Tuple <byte, byte, byte>(item.Location, item.X, item.Y)].Item2; talkString = talkString.Replace("<Item>", "the skull").CapitalizeFirstLetter(); person = FindPerson("Jude"); person.Yes = talkString; item = ultimaData.Items[ultimaData.ITEM_NIGHTSHADE]; talkString = talkToLocation[new Tuple <byte, byte, byte>(item.Location, item.X, item.Y)].Item2; talkString = talkString.Replace("<Item>", "nightshade").CapitalizeFirstLetter(); person = FindPerson("Virgil"); person.KeywordResponse2 = talkString; item = ultimaData.Items[ultimaData.ITEM_MANDRAKE]; talkString = talkToLocation[new Tuple <byte, byte, byte>(item.Location, item.X, item.Y)].Item1; talkString = talkString.Replace("<Item>", "mandrake").CapitalizeFirstLetter(); person = FindPerson("Calumny"); person.KeywordResponse2 = talkString; item = ultimaData.Items[ultimaData.ITEM_HORN]; talkString = talkToLocation[new Tuple <byte, byte, byte>(item.Location, item.X, item.Y)].Item1; talkString = talkString.Replace("<Item>", "the silver horn").CapitalizeFirstLetter(); person = FindPerson("Malchor"); person.KeywordResponse2 = talkString; item = ultimaData.Items[ultimaData.ITEM_WHEEL]; talkString = talkToLocation[new Tuple <byte, byte, byte>(item.Location, item.X, item.Y)].Item1; talkString = talkString.Replace("<Item>", "the magical wheel").CapitalizeFirstLetter(); person = FindPerson("Lassorn"); person.KeywordResponse2 = talkString; item = ultimaData.Items[ultimaData.ITEM_BLACK_STONE]; talkString = talkToLocation[new Tuple <byte, byte, byte>(item.Location, item.X, item.Y)].Item2; talkString = talkString.Replace("<Item>", "the black stone").CapitalizeFirstLetter(); person = FindPerson("Merlin"); person.KeywordResponse1 = talkString; item = ultimaData.Items[ultimaData.ITEM_WHITE_STONE]; talkString = talkToLocation[new Tuple <byte, byte, byte>(item.Location, item.X, item.Y)].Item1; talkString = talkString.Replace("<Item>", "the white stone").CapitalizeFirstLetter(); person = FindPerson("Isaac"); person.KeywordResponse2 = talkString; ultimaData.ShrineText[6 * 3 + 2] = "If thou dost seek the White Stone rest at the Inn of Spirits."; } if (flags.NoRequireFullParty) { ultimaData.LBHelpText[18] = "Thou dost now seem ready to make the final journey into the dark Abyss!\n"; } // --- Fixes --- if (flags.Fixes) { var person = FindPerson("Water"); person.QuestionFlag = 6; SpoilerLog.Add(SpoilerCategory.Fix, $"Water asks question"); person = FindPerson("Estro"); person.Keyword1 = "RESE"; SpoilerLog.Add(SpoilerCategory.Fix, $"Estro keyword fix"); person = FindPerson("a truth\nseeker."); person.KeywordResponse2 = person.KeywordResponse2.Replace("minutes", "cycles"); SpoilerLog.Add(SpoilerCategory.Fix, $"a truth seeker word usage"); person = FindPerson("Catriona"); person.Yes = person.Yes + "."; SpoilerLog.Add(SpoilerCategory.Fix, $"Catriona punctuation"); person = FindPerson("a ranger."); person.Yes = person.Yes.Replace("knowns", "knows"); SpoilerLog.Add(SpoilerCategory.Fix, $"Ranger typo"); person = FindPerson("Calabrini"); person.Keyword2 = "INJU"; person.Question = "Dost thou seek\nan inn or art\nthou injured?"; SpoilerLog.Add(SpoilerCategory.Fix, $"Calabrini heal keyword"); person = FindPerson("Michelle"); person.No = "Then thou should\nvisit our\nphysician!"; person.Keyword2 = "PHYS"; person.KeywordResponse1 = person.KeywordResponse1.Replace("west", "north"); person.KeywordResponse2 = "Got north and take\nthe western door."; SpoilerLog.Add(SpoilerCategory.Fix, $"Michelle heal keyword"); person = FindPerson("Tracie"); person.Look = "A starving journalist"; SpoilerLog.Add(SpoilerCategory.Fix, $"Tracie corrected look"); person = FindPerson("Iolo"); person.Look = "A charming bard"; SpoilerLog.Add(SpoilerCategory.Fix, $"Iolo corrected look"); person = FindPerson("Sir William"); person.KeywordResponse2 = person.KeywordResponse2.Replace("never", "Never"); SpoilerLog.Add(SpoilerCategory.Fix, $"Sir William capitalization"); person = FindPerson("Alkerion"); person.QuestionFlag = 6; SpoilerLog.Add(SpoilerCategory.Fix, $"Alkerion asks question"); person = FindPerson("Dupre"); person.Look = "A handsome fighter"; SpoilerLog.Add(SpoilerCategory.Fix, $"Dupre corrected look"); person = FindPerson("Virgil"); person.Question = "Is it thine?"; SpoilerLog.Add(SpoilerCategory.Fix, $"Virgil question grammar"); person = FindPerson("Shamino"); person.QuestionFlag = 6; person = FindPerson("Traveling Dan"); person.Look = "A short, rotund\nman with a hat\nand vest."; SpoilerLog.Add(SpoilerCategory.Fix, $"Traveling Dan corrected look"); person = FindPerson("Charm"); person.QuestionFlag = 6; SpoilerLog.Add(SpoilerCategory.Fix, $"Charm asks question"); person = FindPerson("Rabindranath\ntagore"); person.Name = "Rabindranath\nTagore"; SpoilerLog.Add(SpoilerCategory.Fix, $"Fix 'Rabindranath tagore' capitalization"); } }
public void Load(string path, UltimaData data) { var file = Path.Combine(path, filename); FileHelper.TryBackupOriginalFile(file); // Apply delta file to create new file var newFilePath2 = file; var newFileOutputDirectory = Path.GetDirectoryName(newFilePath2); if (!Directory.Exists(newFileOutputDirectory)) { Directory.CreateDirectory(newFileOutputDirectory); } var deltaApplier = new DeltaApplier { SkipHashCheck = false }; using (var basisStream = new FileStream($"{file}.orig", FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var deltaStream = new MemoryStream(Patches.TITLE_EXE)) { using (var newFileStream = new FileStream(newFilePath2, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) { deltaApplier.Apply(basisStream, new BinaryDeltaReader(deltaStream, new ConsoleProgressReporter()), newFileStream); } } } using (var titleStream = new System.IO.FileStream(file, System.IO.FileMode.Open)) { titleBytes = titleStream.ReadAllBytes(); } if (titleBytes[START_X_OFFSET] != 0xE7) { throw new Exception($"Offset START_X_OFFSET appears to be wrong."); } if (titleBytes[START_Y_OFFSET] != 0x88) { throw new Exception($"Offset START_Y_OFFSET appears to be wrong."); } if (titleBytes[FLAG_ENCODE_OFFSET] != 0x43) { throw new Exception($"Offset FLAG_ENCODE_OFFSET appears to be wrong."); } if (titleBytes[ENABLE_KARMA_OVERRIDE_OFFSET] != 0x09) { throw new Exception($"Offset ENABLE_KARMA_OVERRIDE_OFFSET appears to be wrong."); } if (titleBytes[KARMA_OVERRIDE_VALUES_OFFSET] != 0xFF) { throw new Exception($"Offset KARMA_OVERRIDE_VALUES_OFFSET appears to be wrong."); } for (int offset = 0; offset < 8; offset++) { data.StartingPositions.Add(new Coordinate(titleBytes[START_X_OFFSET + offset], titleBytes[START_Y_OFFSET + offset])); } for (int offset = 0; offset < 8; offset++) { data.StartingKarma.Add(titleBytes[KARMA_OVERRIDE_VALUES_OFFSET + offset]); } }
public void Load(string path, UltimaData data, IWorldMap worldMap, Flags flags) { var file = Path.Combine(path, filename); if (flags.VGAPatch && HashHelper.BytesToString(HashHelper.GetHashSha256(file)) == upgradeFileHash) { DowngradeVGAPatch(file); } FileHelper.TryBackupOriginalFile(file); // Apply delta file to create new file var newFilePath2 = file; var newFileOutputDirectory = Path.GetDirectoryName(newFilePath2); if (!Directory.Exists(newFileOutputDirectory)) { Directory.CreateDirectory(newFileOutputDirectory); } var deltaApplier = new DeltaApplier { SkipHashCheck = false }; using (var basisStream = new FileStream($"{file}.orig", FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var deltaStream = new MemoryStream(Patches.AVATAR_EXE)) { using (var newFileStream = new FileStream(newFilePath2, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) { deltaApplier.Apply(basisStream, new BinaryDeltaReader(deltaStream, new ConsoleProgressReporter()), newFileStream); } } } using (var avatarStream = new System.IO.FileStream(file, System.IO.FileMode.Open)) { avatarBytes = avatarStream.ReadAllBytes(); } AvatarOffset = new AvatarOffsetsNew(avatarBytes, $"{file}.orig"); // Items var items = new List <Item>(); for (int offset = 0; offset < 23; offset++) { items.Add(new Item(avatarBytes[AvatarOffset.ITEM_LOCATIONS_OFFSET + offset * 5], avatarBytes[AvatarOffset.ITEM_LOCATIONS_OFFSET + offset * 5 + 1], avatarBytes[AvatarOffset.ITEM_LOCATIONS_OFFSET + offset * 5 + 2])); } data.SetItems(items); // Moongates var moongates = new List <Tile>(); for (byte offset = 0; offset < 8; offset++) { moongates.Add(worldMap.GetCoordinate(avatarBytes[AvatarOffset.MOONGATE_X_OFFSET + offset], avatarBytes[AvatarOffset.MOONGATE_Y_OFFSET + offset])); } data.SetMoongates(moongates); // LCB var lcb = new List <Tile>(); var lcbLoc = worldMap.GetCoordinate(avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_LCB - 1], avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_LCB - 1]); lcb.Add(lcbLoc); lcb.Add(worldMap.GetCoordinate(lcbLoc.X - 1, lcbLoc.Y)); lcb.Add(worldMap.GetCoordinate(lcbLoc.X + 1, lcbLoc.Y)); data.SetLCB(lcb); // Castles var castles = new List <TileDirtyWrapper>(); for (byte offset = 0; offset < 3; offset++) { castles.Add(new TileDirtyWrapper(worldMap.GetCoordinate(avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_CASTLES + offset], avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_CASTLES + offset]), worldMap)); } data.SetCastles(castles); // Towns var towns = new List <TileDirtyWrapper>(); for (byte offset = 0; offset < 8 + 4; offset++) { towns.Add(new TileDirtyWrapper(worldMap.GetCoordinate(avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_TOWNS + offset - 1], avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_TOWNS + offset - 1]), worldMap)); } data.SetTowns(towns); // Shrines var shrines = new List <TileDirtyWrapper>(); for (byte offset = 0; offset < 8; offset++) { shrines.Add(new TileDirtyWrapper(worldMap.GetCoordinate(avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_SHRINES + offset - 1], avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_SHRINES + offset - 1]), worldMap)); } data.SetShrines(shrines); // Dungeons var dungeons = new List <Tile>(); for (byte offset = 0; offset < 8; offset++) { dungeons.Add(worldMap.GetCoordinate(avatarBytes[AvatarOffset.AREA_X_OFFSET + data.LOC_DUNGEONS + offset - 1], avatarBytes[AvatarOffset.AREA_Y_OFFSET + data.LOC_DUNGEONS + offset - 1])); } data.SetDungeons(dungeons); // Balloon Spawn data.BalloonSpawn = worldMap.GetCoordinate(avatarBytes[AvatarOffset.BALLOON_SPAWN_LOCATION_X_OFFSET], avatarBytes[AvatarOffset.BALLOON_SPAWN_LOCATION_Y_OFFSET]); OriginalShrineText = new List <string>(); OriginalShrineTextStartOffset = new List <int>(); var shrineTextBytes = new List <byte>(); var textOffset = AvatarOffset.SHRINE_TEXT_OFFSET; for (int i = 0; i < 24; i++) { OriginalShrineTextStartOffset.Add(textOffset); for (; avatarBytes[textOffset] != 0x0A && avatarBytes[textOffset] != 0x00; textOffset++) { shrineTextBytes.Add(avatarBytes[textOffset]); } OriginalShrineText.Add(System.Text.Encoding.Default.GetString(shrineTextBytes.ToArray())); shrineTextBytes.Clear(); if (avatarBytes[textOffset] == 0x0A) { textOffset++; } textOffset++; } data.ShrineText.Clear(); data.ShrineText.AddRange(OriginalShrineText); OriginalLBText = new List <string>(); OriginalLBTextStartOffset = new List <int>(); var lbTextBytes = new List <byte>(); textOffset = AvatarOffset.LB_TEXT_OFFSET; // He has more text than 19 but there is some weird stuff after 19 that doesn't get turned into text well. And as far as I can tell we won't need any text after 19 for (int i = 0; i < 19; i++) { OriginalLBTextStartOffset.Add(textOffset); for (; avatarBytes[textOffset] != 0x00 && avatarBytes[textOffset] != 0xAB; textOffset++) { lbTextBytes.Add(avatarBytes[textOffset]); } OriginalLBText.Add(System.Text.Encoding.Default.GetString(lbTextBytes.ToArray())); lbTextBytes.Clear(); if (avatarBytes[textOffset] == 0x0A || avatarBytes[textOffset] == 0xAB) { textOffset++; } textOffset++; } data.LBText.Clear(); data.LBText.AddRange(OriginalLBText); OriginalLBHelpText = new List <string>(); OriginalLBHelpTextStartOffset = new List <int>(); lbTextBytes = new List <byte>(); textOffset = AvatarOffset.LB_HELP_TEXT_OFFSET; for (int i = 0; i < 21; i++) { OriginalLBHelpTextStartOffset.Add(textOffset); for (; avatarBytes[textOffset] != 0x00 && avatarBytes[textOffset] != 0xAB; textOffset++) { lbTextBytes.Add(avatarBytes[textOffset]); } OriginalLBHelpText.Add(System.Text.Encoding.Default.GetString(lbTextBytes.ToArray())); lbTextBytes.Clear(); if (avatarBytes[textOffset] == 0x0A || avatarBytes[textOffset] == 0xAB) { textOffset++; } textOffset++; } data.LBHelpText.Clear(); data.LBHelpText.AddRange(OriginalLBHelpText); var mantraTextBytes = new List <byte>(); textOffset = AvatarOffset.MANTRA_OFFSET; MantraMaxSize = 0; for (int i = 0; i < 8; i++) { for (; avatarBytes[textOffset] != 0x00; textOffset++) { mantraTextBytes.Add(avatarBytes[textOffset]); } data.Mantras.Add(System.Text.Encoding.Default.GetString(mantraTextBytes.ToArray())); MantraMaxSize += data.Mantras[i].Length + 1; mantraTextBytes.Clear(); textOffset++; } var wordOfPassageTextBytes = new List <byte>(); for (int offSet = 0; offSet < 9; offSet++) { wordOfPassageTextBytes.Add(avatarBytes[AvatarOffset.WORD_OF_PASSAGE + offSet]); } data.WordOfPassage = System.Text.Encoding.Default.GetString(wordOfPassageTextBytes.ToArray()); data.DaemonSpawnX1 = avatarBytes[AvatarOffset.DEMON_SPAWN_TRIGGER_X1_OFFSET]; data.DaemonSpawnX2 = avatarBytes[AvatarOffset.DEMON_SPAWN_TRIGGER_X2_OFFSET]; data.DaemonSpawnY1 = avatarBytes[AvatarOffset.DEMON_SPAWN_TRIGGER_Y1_OFFSET]; data.DaemonSpawnY2 = avatarBytes[AvatarOffset.DEMON_SPAWN_TRIGGER_Y2_OFFSET]; data.DaemonSpawnLocationX = avatarBytes[AvatarOffset.DEMON_SPAWN_LOCATION_X_OFFSET]; for (int i = 0; i < 8; i++) { data.PirateCove.Add(new Coordinate(avatarBytes[i + AvatarOffset.PIRATE_COVE_X_OFFSET], avatarBytes[i + AvatarOffset.PIRATE_COVE_Y_OFFSET])); } data.PirateCoveSpawnTrigger = new Coordinate(avatarBytes[AvatarOffset.PIRATE_COVE_SPAWN_TRIGGER_X_OFFSET1], avatarBytes[AvatarOffset.PIRATE_COVE_SPAWN_TRIGGER_Y_OFFSET1]); data.WhirlpoolExit = new Coordinate(avatarBytes[AvatarOffset.WHIRLPOOL_EXIT_X_OFFSET], avatarBytes[AvatarOffset.WHIRLPOOL_EXIT_Y_OFFSET]); data.SpellsRecipes = new List <ByteDirtyWrapper>(); for (int i = 0; i < 26; i++) { data.SpellsRecipes.Add(new ByteDirtyWrapper(avatarBytes[AvatarOffset.SPELL_RECIPE_OFFSET + i])); } data.BlinkCastExclusionX1 = avatarBytes[AvatarOffset.BLINK_CAST_EXCLUSION_X1_OFFSET]; data.BlinkCastExclusionX2 = avatarBytes[AvatarOffset.BLINK_CAST_EXCLUSION_X2_OFFSET]; data.BlinkCastExclusionY1 = avatarBytes[AvatarOffset.BLINK_CAST_EXCLUSION_Y1_OFFSET]; data.BlinkCastExclusionY2 = avatarBytes[AvatarOffset.BLINK_CAST_EXCLUSION_Y2_OFFSET]; data.BlinkDestinationExclusionX1 = avatarBytes[AvatarOffset.BLINK_DESTINATION_EXCLUSION_X1_OFFSET]; data.BlinkDestinationExclusionX2 = avatarBytes[AvatarOffset.BLINK_DESTINATION_EXCLUSION_X2_OFFSET]; data.BlinkDestinationExclusionY1 = avatarBytes[AvatarOffset.BLINK_DESTINATION_EXCLUSION_Y1_OFFSET]; data.BlinkDestinationExclusionY2 = avatarBytes[AvatarOffset.BLINK_DESTINATION_EXCLUSION_Y2_OFFSET]; data.BlinkDestinationExclusion2X1 = avatarBytes[AvatarOffset.BLINK_DESTINATION2_EXCLUSION_X1_OFFSET]; data.BlinkDestinationExclusion2X2 = avatarBytes[AvatarOffset.BLINK_DESTINATION2_EXCLUSION_X2_OFFSET]; data.BlinkDestinationExclusion2Y1 = avatarBytes[AvatarOffset.BLINK_DESTINATION2_EXCLUSION_Y1_OFFSET]; data.BlinkDestinationExclusion2Y2 = avatarBytes[AvatarOffset.BLINK_DESTINATION2_EXCLUSION_Y2_OFFSET]; for (int i = 0; i < 13; i++) { data.AbyssEjectionLocations.Add(new Coordinate(avatarBytes[i + AvatarOffset.ABYSS_EJECTION_LOCATIONS_X], avatarBytes[i + AvatarOffset.ABYSS_EJECTION_LOCATIONS_Y])); } for (int townIdx = 0; townIdx < 16; townIdx++) { data.ShopLocations.Add(new List <byte>()); for (int shopIdx = 0; shopIdx < 8; shopIdx++) { data.ShopLocations[townIdx].Add(avatarBytes[townIdx * 8 + shopIdx + AvatarOffset.SHOP_LOCATION_OFFSET]); } } }
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 ultimaData, Flags flags) { }
public void Load(string path, UltimaData data) { var file = Path.Combine(path, filename); FileHelper.TryBackupOriginalFile(file); using (var partyStream = new System.IO.FileStream($"{file}.orig", System.IO.FileMode.Open)) { partyBytes = partyStream.ReadAllBytes(); } for (int i = 0; i < 8; i++) { var character = new Character(); character.Hp = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_HP_OFFSET); character.MaxHp = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_MAX_HP_OFFSET); character.XP = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_XP_OFFSET); character.Str = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_STR_OFFSET); character.Dex = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_DEX_OFFSET); character.Int = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_INT_OFFSET); character.Mp = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_MP_OFFSET); character.Weapon = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_WEAPON_OFFSET); character.Armor = BitConverter.ToUInt16(partyBytes, CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_ARMOR_OFFSET); var nameTextBytes = new List <byte>(); var textOffset = CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_NAME_OFFSET; for (; partyBytes[textOffset] != 0x00; textOffset++) { nameTextBytes.Add(partyBytes[textOffset]); } character.Name = System.Text.Encoding.Default.GetString(nameTextBytes.ToArray()); nameTextBytes.Clear(); character.Sex = (partyBytes[CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_SEX_OFFSET] == 0xb ? 'M' : 'F'); character.Class = partyBytes[CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_CLASS_OFFSET]; character.Status = (char)partyBytes[CHARACTER_RECORDS_OFFSET + (i * CHAR_RECORD_LENGTH) + CHARACTER_STATUS_OFFSET]; data.StartingCharacters.Add(character); } data.StartingFood = BitConverter.ToUInt32(partyBytes, FOOD_OFFSET); data.StartingGold = BitConverter.ToUInt16(partyBytes, GOLD_OFFSET); data.StartingItems = BitConverter.ToUInt16(partyBytes, ITEMS1_OFFSET); for (int offset = 0; offset < 4; offset++) { data.StartingEquipment.Add(BitConverter.ToUInt16(partyBytes, EQUIPMENT_OFFSET + (offset * 2))); } for (int offset = 0; offset < 8; offset++) { data.StartingArmor.Add(BitConverter.ToUInt16(partyBytes, ARMOR_OFFSET + (offset * 2))); } for (int offset = 0; offset < 16; offset++) { data.StartingWeapons.Add(BitConverter.ToUInt16(partyBytes, WEAPONS_OFFSET + (offset * 2))); } for (int offset = 0; offset < 8; offset++) { data.StartingReagents.Add(BitConverter.ToUInt16(partyBytes, REAGENTS_OFFSET + (offset * 2))); } for (int offset = 0; offset < 26; offset++) { data.StartingMixtures.Add(BitConverter.ToUInt16(partyBytes, MIXTURES_OFFSET + (offset * 2))); } data.StartingStones = partyBytes[STONES_OFFSET]; data.StartingRunes = partyBytes[RUNES_OFFSET]; }
private static void LogQuestItems(SpoilerLog spoilerLog, UltimaData ultimaData) { ushort ushortone = 1; var startingItems = new List <string> { "Skull", "Skull destroyed", "Candle", "Book", "Bell", "Courage Key", "Love Key", "Truth Key", "Silver Horn", "Wheel of the H.M.S. Cape", "Candle used", "Book used", "Bell used" }; for (int i = 0; i < startingItems.Count; i++) { if ((ultimaData.StartingItems & (ushort)(ushortone << i)) != 0) { spoilerLog.Add(SpoilerCategory.Start, startingItems[i]); } else { spoilerLog.Add(SpoilerCategory.Start, $"No {startingItems[i]}"); } } for (int i = 0; i < 8; i++) { if ((ultimaData.StartingRunes & (ushort)(ushortone << i)) != 0) { spoilerLog.Add(SpoilerCategory.Start, ultimaData.ItemNames[i + 15]); } else { spoilerLog.Add(SpoilerCategory.Start, $"No {ultimaData.ItemNames[i + 15]}"); } } var startingStones = new List <string> { "Blue", "Yellow", "Red", "Green", "Orange", "Purple", "White", "Black" }; for (int i = 0; i < startingStones.Count; i++) { if ((ultimaData.StartingStones & (ushort)(ushortone << i)) != 0) { spoilerLog.Add(SpoilerCategory.Start, startingStones[i]); } else { spoilerLog.Add(SpoilerCategory.Start, $"No {startingStones[i]}"); } } }