}; // Warp, Soft, Exit, Life, Life 2 private void WriteItemSpellData(MagicSpell Spell, Item item) { // Set the spell an item casts var output = Spell.Data.ToHex().Remove(2); var offset = WeaponOffset + 0x8 * Math.Min((byte)item - WeaponStart, ArmourStart - WeaponStart) + 0x4 * Math.Max(0, (byte)item - ArmourStart) + MagicBitOffset; Put(offset, Blob.FromHex(output)); // if the item is the Defense, overwrite the last character in the name, otherwise, keep the icon there. output = Spell.Name.ToHex().Remove(12) + (item == Item.Defense ? "FF" : ""); offset = GearTextOffset + ((byte)item > (byte)Item.Ribbon ? 1 : 0) + GearTextSize * ((byte)item - WeaponStart); Put(offset, Blob.FromHex(output)); }
private void WriteItemSpellData(MagicSpell Spell, Item item) { // Set the spell an item casts var offset = WeaponOffset + 0x8 * Math.Min((byte)item - WeaponStart, ArmorStart - WeaponStart) + 0x4 * Math.Max(0, (byte)item - ArmorStart) + MagicBitOffset; Data[offset] = (byte)(Spell.Index + 1); if (ItemsText[(int)item].Contains("@")) { ItemsText[(int)item] = ItemsText[(int)item].Remove(0, 6).Insert(0, Spell.Name.PadRight(6)); } else { ItemsText[(int)item] = Spell.Name.PadRight(7); } }
private void WriteItemSpellData(MagicSpell Spell, Item item) { // Set the spell an item casts var offset = WeaponOffset + 0x8 * Math.Min((byte)item - WeaponStart, ArmorStart - WeaponStart) + 0x4 * Math.Max(0, (byte)item - ArmorStart) + MagicBitOffset; Data[offset] = (byte)(Spell.Index + 1); // Setup the text of the item's name to include the spell name. offset = GearTextOffset + ((byte)item > (byte)Item.Ribbon ? 1 : 0) + GearTextSize * ((byte)item - WeaponStart); if (Get(offset, 1)[0] > 200) { offset++; // If the first byte is in the icon range, bump the pointer to overwrite after it. } else if (Get(offset + 6, 1)[0] <= 200) { Data[offset + 6] = 0xFF; // Erase final non-icon characters from name. } Debug.Assert(Spell.Name.Length == 6); Put(offset, Spell.Name); }
private void WriteItemSpellData(MagicSpell Spell, Item item) { // Set the spell an item casts var offset = WeaponOffset + 0x8 * Math.Min((byte)item - WeaponStart, ArmorStart - WeaponStart) + 0x4 * Math.Max(0, (byte)item - ArmorStart) + MagicBitOffset; Data[offset] = (byte)(Spell.Index + 1); // Setup the text of the item's name to include the spell name. offset = GearTextOffset + ((byte)item > (byte)Item.Ribbon ? 1 : 0) + GearTextSize * ((byte)item - WeaponStart); if (Get(offset, 1)[0] > 200) { offset++; // If the first byte is in the icon range, bump the pointer to overwrite after it. } else if (Get(offset + 6, 1)[0] <= 200) { Data[offset + 6] = 0xFF; // Erase final non-icon characters from name. } // Fix up the name of the spell so it works as part of an item name. var fixedSpellName = FF1Text.TextToBytes(FF1Text.BytesToText(Spell.Name).PadRight(6), false, FF1Text.Delimiter.Empty); Debug.Assert(fixedSpellName.Length == 6); Put(offset, fixedSpellName); }
public void ShuffleMagicLevels(MT19337 rng, bool keepPermissions, bool tieredShuffle, bool mixSpellbooks, bool noSpellcrafter) { var magicSpells = GetSpells(); if (tieredShuffle && noSpellcrafter) { // if we are doing a tiered shuffle, swap the position of TMPR and SABR before further shuffling for balance purposes MagicSpell tmpTMPR = magicSpells[14]; magicSpells[14] = magicSpells[54]; magicSpells[54] = tmpTMPR; } // First we have to un-interleave white and black spells. var whiteSpells = magicSpells.Where((spell, i) => (i / 4) % 2 == 0).ToList(); var blackSpells = magicSpells.Where((spell, i) => (i / 4) % 2 == 1).ToList(); if (tieredShuffle) { // weigh spell probability of landing in a tier based on where it was in the original game var whiteSpellList = new List <MagicSpell> [3]; var blackSpellList = new List <MagicSpell> [3]; var whiteSpellFinalList = new List <MagicSpell> [3]; var blackSpellFinalList = new List <MagicSpell> [3]; int mergedSpellDoubler = 1; whiteSpellList[0] = magicSpells.Where((spell, i) => (i / 4) % 2 == 0 && i < 24).ToList(); whiteSpellList[1] = magicSpells.Where((spell, i) => (i / 4) % 2 == 0 && i < 48 && i >= 24).ToList(); whiteSpellList[2] = magicSpells.Where((spell, i) => (i / 4) % 2 == 0 && i >= 48).ToList(); blackSpellList[0] = magicSpells.Where((spell, i) => (i / 4) % 2 == 1 && i < 24).ToList(); blackSpellList[1] = magicSpells.Where((spell, i) => (i / 4) % 2 == 1 && i < 48 && i >= 24).ToList(); blackSpellList[2] = magicSpells.Where((spell, i) => (i / 4) % 2 == 1 && i >= 48).ToList(); if (mixSpellbooks) { whiteSpellList[0] = whiteSpellList[0].Concat(blackSpellList[0]).ToList(); whiteSpellList[1] = whiteSpellList[1].Concat(blackSpellList[1]).ToList(); whiteSpellList[2] = whiteSpellList[2].Concat(blackSpellList[2]).ToList(); mergedSpellDoubler = 2; } whiteSpellFinalList[0] = new List <MagicSpell> { }; whiteSpellFinalList[1] = new List <MagicSpell> { }; whiteSpellFinalList[2] = new List <MagicSpell> { }; blackSpellFinalList[0] = new List <MagicSpell> { }; blackSpellFinalList[1] = new List <MagicSpell> { }; blackSpellFinalList[2] = new List <MagicSpell> { }; whiteSpells.Clear(); blackSpells.Clear(); foreach (MagicSpell spell in whiteSpellList[2]) { // 70% chance of tier 7-8, 25% chance of tier 4-6, 5% chance of tier 1-3 int diceRoll = rng.Between(0, 19); if (diceRoll < 14) { whiteSpellFinalList[2].Add(spell); } else if (diceRoll < 19) { whiteSpellFinalList[1].Add(spell); } else { whiteSpellFinalList[0].Add(spell); } } foreach (MagicSpell spell in whiteSpellList[1]) { // 60% chance of tier 4-6, 25% chance of tier 1-3, 15% chance of tier 7-8 // if a section of the final list is full, move to another section int diceRoll = rng.Between(0, 19); if (diceRoll < 12) { if (whiteSpellFinalList[1].Count >= 12 * mergedSpellDoubler) { if (whiteSpellFinalList[0].Count >= 12 * mergedSpellDoubler) { whiteSpellFinalList[2].Add(spell); } else { whiteSpellFinalList[0].Add(spell); } } else { whiteSpellFinalList[1].Add(spell); } } else if (diceRoll < 17) { if (whiteSpellFinalList[0].Count >= 12 * mergedSpellDoubler) { if (whiteSpellFinalList[1].Count >= 12 * mergedSpellDoubler) { whiteSpellFinalList[2].Add(spell); } else { whiteSpellFinalList[1].Add(spell); } } else { whiteSpellFinalList[0].Add(spell); } } else { if (whiteSpellFinalList[2].Count >= 8 * mergedSpellDoubler) { if (whiteSpellFinalList[1].Count >= 12 * mergedSpellDoubler) { whiteSpellFinalList[0].Add(spell); } else { whiteSpellFinalList[1].Add(spell); } } else { whiteSpellFinalList[2].Add(spell); } } } foreach (MagicSpell spell in whiteSpellList[0]) { // fill the remaining tiers with the tier 1-3 base magic if (whiteSpellFinalList[0].Count >= 12 * mergedSpellDoubler) { if (whiteSpellFinalList[1].Count >= 12 * mergedSpellDoubler) { whiteSpellFinalList[2].Add(spell); } else { whiteSpellFinalList[1].Add(spell); } } else { whiteSpellFinalList[0].Add(spell); } } // and repeat the process for black magic if we didn't mix spellbooks if (mixSpellbooks) { // if we mixed spellbooks, split the white (merged) spellbook in halves to set the black spell list blackSpellFinalList[0] = whiteSpellFinalList[0].Take(12).ToList(); whiteSpellFinalList[0] = whiteSpellFinalList[0].Except(blackSpellFinalList[0]).ToList(); blackSpellFinalList[1] = whiteSpellFinalList[1].Take(12).ToList(); whiteSpellFinalList[1] = whiteSpellFinalList[1].Except(blackSpellFinalList[1]).ToList(); blackSpellFinalList[2] = whiteSpellFinalList[2].Take(8).ToList(); whiteSpellFinalList[2] = whiteSpellFinalList[2].Except(blackSpellFinalList[2]).ToList(); } else { foreach (MagicSpell spell in blackSpellList[2]) { // 70% chance of tier 7-8, 25% chance of tier 4-6, 5% chance of tier 1-3 int diceRoll = rng.Between(0, 19); if (diceRoll < 14) { blackSpellFinalList[2].Add(spell); } else if (diceRoll < 19) { blackSpellFinalList[1].Add(spell); } else { blackSpellFinalList[0].Add(spell); } } foreach (MagicSpell spell in blackSpellList[1]) { // 60% chance of tier 4-6, 25% chance of tier 1-3, 15% chance of tier 7-8 // if a section of the final list is full, move to another section int diceRoll = rng.Between(0, 19); if (diceRoll < 12) { if (blackSpellFinalList[1].Count >= 12) { if (blackSpellFinalList[0].Count >= 12) { blackSpellFinalList[2].Add(spell); } else { blackSpellFinalList[0].Add(spell); } } else { blackSpellFinalList[1].Add(spell); } } else if (diceRoll < 17) { if (blackSpellFinalList[0].Count >= 12) { if (blackSpellFinalList[1].Count >= 12) { blackSpellFinalList[2].Add(spell); } else { blackSpellFinalList[1].Add(spell); } } else { blackSpellFinalList[0].Add(spell); } } else { if (blackSpellFinalList[2].Count >= 8) { if (blackSpellFinalList[1].Count >= 12) { blackSpellFinalList[0].Add(spell); } else { blackSpellFinalList[1].Add(spell); } } else { blackSpellFinalList[2].Add(spell); } } } foreach (MagicSpell spell in blackSpellList[0]) { // fill the remaining tiers with the tier 1-3 base magic if (blackSpellFinalList[0].Count >= 12) { if (blackSpellFinalList[1].Count >= 12) { blackSpellFinalList[2].Add(spell); } else { blackSpellFinalList[1].Add(spell); } } else { blackSpellFinalList[0].Add(spell); } } } // shuffle each of the final lists foreach (List <MagicSpell> spellList in whiteSpellFinalList) { spellList.Shuffle(rng); } if (!mixSpellbooks) { foreach (List <MagicSpell> spellList in blackSpellFinalList) { spellList.Shuffle(rng); } } // and append each in turn to the whitespells / blackspells list whiteSpells = whiteSpells.Concat(whiteSpellFinalList[0]).ToList(); whiteSpells = whiteSpells.Concat(whiteSpellFinalList[1]).ToList(); whiteSpells = whiteSpells.Concat(whiteSpellFinalList[2]).ToList(); blackSpells = blackSpells.Concat(blackSpellFinalList[0]).ToList(); blackSpells = blackSpells.Concat(blackSpellFinalList[1]).ToList(); blackSpells = blackSpells.Concat(blackSpellFinalList[2]).ToList(); } else { if (mixSpellbooks) { var mergedList = magicSpells.ToList(); mergedList.Shuffle(rng); whiteSpells = mergedList.Where((spell, i) => (i / 4) % 2 == 0).ToList(); blackSpells = mergedList.Where((spell, i) => (i / 4) % 2 == 1).ToList(); } else { whiteSpells.Shuffle(rng); blackSpells.Shuffle(rng); } } // Now we re-interleave the spells. var shuffledSpells = new List <MagicSpell>(); for (int i = 0; i < MagicCount; i++) { var sourceIndex = 4 * (i / 8) + i % 4; if ((i / 4) % 2 == 0) { shuffledSpells.Add(whiteSpells[sourceIndex]); } else { shuffledSpells.Add(blackSpells[sourceIndex]); } } Put(MagicOffset, shuffledSpells.Select(spell => spell.Data).Aggregate((seed, next) => seed + next)); Put(MagicNamesOffset, shuffledSpells.Select(spell => spell.Name).Aggregate((seed, next) => seed + next)); Put(MagicTextPointersOffset, shuffledSpells.Select(spell => spell.TextPointer).ToArray()); if (keepPermissions) { // Shuffle the permissions the same way the spells were shuffled. for (int c = 0; c < MagicPermissionsCount; c++) { var oldPermissions = Get(MagicPermissionsOffset + c * MagicPermissionsSize, MagicPermissionsSize); var newPermissions = new byte[MagicPermissionsSize]; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { var oldIndex = shuffledSpells[8 * i + j].Index; var oldPermission = (oldPermissions[oldIndex / 8] & (0x80 >> oldIndex % 8)) >> (7 - oldIndex % 8); newPermissions[i] |= (byte)(oldPermission << (7 - j)); } } Put(MagicPermissionsOffset + c * MagicPermissionsSize, newPermissions); } } // Map old indices to new indices. var newIndices = new byte[MagicCount]; for (byte i = 0; i < MagicCount; i++) { newIndices[shuffledSpells[i].Index] = i; } // Fix enemy spell pointers to point to where the spells are now. var scripts = Get(ScriptOffset, ScriptSize * ScriptCount).Chunk(ScriptSize); foreach (var script in scripts) { // Bytes 2-9 are magic spells. for (int i = 2; i < 10; i++) { if (script[i] != 0xFF) { script[i] = newIndices[script[i]]; } } } Put(ScriptOffset, scripts.SelectMany(script => script.ToBytes()).ToArray()); // Fix weapon and armor spell pointers to point to where the spells are now. var weapons = Get(WeaponOffset, WeaponSize * WeaponCount).Chunk(WeaponSize); foreach (var weapon in weapons) { if (weapon[3] != 0x00) { weapon[3] = (byte)(newIndices[weapon[3] - 1] + 1); } } Put(WeaponOffset, weapons.SelectMany(weapon => weapon.ToBytes()).ToArray()); var armors = Get(ArmorOffset, ArmorSize * ArmorCount).Chunk(ArmorSize); foreach (var armor in armors) { if (armor[3] != 0x00) { armor[3] = (byte)(newIndices[armor[3] - 1] + 1); } } Put(ArmorOffset, armors.SelectMany(armor => armor.ToBytes()).ToArray()); // Fix the crazy out of battle spell system. var outOfBattleSpellOffset = MagicOutOfBattleOffset; for (int i = 0; i < MagicOutOfBattleCount; i++) { var oldSpellIndex = Data[outOfBattleSpellOffset] - 0xB0; var newSpellIndex = newIndices[oldSpellIndex]; Put(outOfBattleSpellOffset, new[] { (byte)(newSpellIndex + 0xB0) }); outOfBattleSpellOffset += MagicOutOfBattleSize; } // Confused enemies are supposed to cast FIRE, so figure out where FIRE ended up. var newFireSpellIndex = shuffledSpells.FindIndex(spell => spell.Data == magicSpells[FireSpellIndex].Data); Put(ConfusedSpellIndexOffset, new[] { (byte)newFireSpellIndex }); }