Skip to content

Commit a1f1d2b

Browse files
authored
Merge pull request #163 from Entroper/dev
Merge dev to master (2.2.0)
2 parents c23f98c + de99e0d commit a1f1d2b

57 files changed

Lines changed: 3657 additions & 905 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ root = true
44
insert_final_newline = true
55
trim_trailing_whitespace = true
66

7-
[*.{cs,xaml}]
7+
[*.{cs,xaml,cshtml}]
88
indent_size = 4
99
indent_style = tab
1010
end_of_line = crlf
1111

12-
[*.{config,csproj,yml,yaml}]
12+
[*.{csproj,yml,yaml}]
1313
indent_size = 2
1414
indent_style = space
1515

16-
[*.{js,cshtml}]
16+
[*.{js}]
1717
indent_size = 4
1818
indent_style = tab
1919
end_of_line = lf

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,6 @@ _Pvt_Extensions
234234

235235
# FAKE - F# Make
236236
.fake/
237+
238+
# ROM files -- never check these in
239+
*.nes

FF1Lib/BugFixes.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public void FixBBAbsorbBug()
8484
PutInBank(0x0B, 0x9966, Blob.FromHex("2046D860"));
8585
PutInBank(0x1F, 0xD846, CreateLongJumpTableEntry(0x0F, 0x8800));
8686
}
87-
87+
8888
public void FixEnemyElementalResistances()
8989
{
9090
// make XFER and other elemental resistance changing spells affect enemies
@@ -97,7 +97,7 @@ public void FixEnemyElementalResistances()
9797
// move 3 byes from previous subroutine and save elemental resistance of the enemy
9898
Put(0x336B5, Blob.FromHex("C89190AD7768A01291906000000000000000")); // extra room at the end for new code
9999
}
100-
100+
101101
public void FixEnemyAOESpells()
102102
{
103103
// Remove comparison and branch on equal which skips the caster when casting aoe spells

FF1Lib/EnemyFormations.cs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,22 @@ namespace FF1Lib
66
{
77
public partial class FF1Rom : NesRom
88
{
9+
public enum FinalFormation
10+
{
11+
WarMECHsAndFriends,
12+
KaryAndTiamat,
13+
TheFundead,
14+
TimeLoop,
15+
};
16+
917
private const int FormationsOffset = 0x2C400;
1018
private const int FormationSize = 16;
1119

12-
private const int FormationCount = 128; // Total formations
13-
private const int NormalFormationCount = 115; // Number of formations before all the boss encounters.
14-
private const int BossFormationCount = 13; // Lichx2, Karyx2, Krakenx2, Tiamatx2, Chaos, Vampire, Astos, Pirates, Garland
15-
private const int VanillaUnrunnableCount = 13; // 13 of the Vanilla normal formations are unrunnable.
20+
private const int FormationCount = 128; // Total formations
21+
private const int NormalFormationCount = 115; // Number of formations before all the boss encounters.
22+
private const int BossFormationCount = 13; // Lichx2, Karyx2, Krakenx2, Tiamatx2, Chaos, Vampire, Astos, Pirates, Garland
23+
private const int VanillaUnrunnableCount = 13; // 13 of the Vanilla normal formations are unrunnable.
24+
private const int ChaosFormationIndex = 123; // Index of Chaos battle that preceeds The End
1625

1726
// Formation Data Offsets ripped straight from Disch's variables.inc
1827
private const byte TypeOffset = 0x00; // battle type (high 4 bits)
@@ -53,6 +62,63 @@ public void ShuffleSurpriseBonus(MT19337 rng)
5362
formations = formations.Zip(chances, (formation, chance) => { formation[SurpriseOffset] = chance; return formation; }).ToList();
5463
Put(FormationsOffset, formations.SelectMany(formation => formation.ToBytes()).ToArray());
5564
}
65+
66+
public void TransformFinalFormation(FinalFormation formation)
67+
{
68+
Blob finalBattle = Get(FormationsOffset + ChaosFormationIndex * FormationSize, FormationSize);
69+
70+
switch (formation)
71+
{
72+
case FinalFormation.WarMECHsAndFriends:
73+
finalBattle[TypeOffset] = 0x2C; // Big/Small Enemy mix, and the Astos/Madpony/Badman/WarMECH patterns
74+
finalBattle[GFXOffset] = 0x03; // WarMECH Badman N/A N/A
75+
finalBattle[IDsOffset + 0] = 0x76; // WarMECH (battle stats, etc)
76+
finalBattle[IDsOffset + 1] = 0x70; // EvilMan
77+
finalBattle[QuantityOffset + 0] = 0x22;
78+
finalBattle[QuantityOffset + 1] = 0x66;
79+
finalBattle[PalettesOffset + 0] = 0x2F;
80+
finalBattle[PalettesOffset + 1] = 0x17;
81+
finalBattle[PaletteAsignmentOffset] = 0x41; // Palette Assignment in top nibble, 1 in bottom for unrunnable.
82+
break;
83+
case FinalFormation.KaryAndTiamat:
84+
finalBattle[TypeOffset] = 0x2B; // Big/Small Enemy mix, and the Dragon2 pattern
85+
finalBattle[GFXOffset] = 0x05; // Dragon Dragon N/A N/A
86+
finalBattle[IDsOffset + 0] = 0x7A; // Kary2
87+
finalBattle[IDsOffset + 1] = 0x7E; // Tiamat2
88+
finalBattle[QuantityOffset + 0] = 0x11;
89+
finalBattle[QuantityOffset + 1] = 0x11;
90+
finalBattle[PalettesOffset + 0] = 0x08;
91+
finalBattle[PalettesOffset + 1] = 0x0A;
92+
finalBattle[PaletteAsignmentOffset] = 0x41; // Palette Assignment in top nibble, 1 in bottom for unrunnable.
93+
break;
94+
case FinalFormation.TheFundead:
95+
finalBattle[TypeOffset] = 0x24; // Eye pattern
96+
finalBattle[GFXOffset] = 0x0B; // Eye / Geist
97+
finalBattle[IDsOffset + 0] = 0x78; // Lich2
98+
finalBattle[IDsOffset + 1] = 0x33; // Phantom
99+
finalBattle[QuantityOffset + 0] = 0x22;
100+
finalBattle[QuantityOffset + 1] = 0x44;
101+
finalBattle[PalettesOffset + 0] = 0x03;
102+
finalBattle[PalettesOffset + 1] = 0x17;
103+
finalBattle[PaletteAsignmentOffset] = 0x41; // Palette Assignment in top nibble, 1 in bottom for unrunnable.
104+
break;
105+
case FinalFormation.TimeLoop:
106+
finalBattle[TypeOffset] = 0x0B; // 9Small + Garland pattern
107+
finalBattle[GFXOffset] = 0x2A; // Garland Garland Garland N/A
108+
finalBattle[IDsOffset + 0] = 0x69; // Garland
109+
finalBattle[IDsOffset + 1] = 0x7F; // Chaos
110+
finalBattle[IDsOffset + 2] = 0x69; // Garland
111+
finalBattle[QuantityOffset + 0] = 0x08;
112+
finalBattle[QuantityOffset + 1] = 0x11;
113+
finalBattle[QuantityOffset + 2] = 0x88;
114+
finalBattle[PalettesOffset + 0] = 0x00;
115+
finalBattle[PalettesOffset + 1] = 0x00;
116+
finalBattle[PaletteAsignmentOffset] = 0x01; // Palette Assignment in top nibble, 1 in bottom for unrunnable.
117+
break;
118+
}
119+
120+
Put(FormationsOffset + ChaosFormationIndex * FormationSize, finalBattle);
121+
}
56122
}
57123

58124
}

FF1Lib/FF1Lib.csproj

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,41 @@
1414
<TreatSpecificWarningsAsErrors />
1515
</PropertyGroup>
1616

17+
<ItemGroup>
18+
<None Remove="presets\beginner.json" />
19+
<None Remove="presets\default.json" />
20+
<None Remove="presets\full-npc.json" />
21+
<None Remove="presets\improved-vanilla.json" />
22+
<None Remove="presets\normal-npc.json" />
23+
<None Remove="presets\tournament.json" />
24+
</ItemGroup>
25+
1726
<ItemGroup>
1827
<Content Include="DTETable.txt">
1928
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
2029
</Content>
30+
<Content Include="presets\beginner.json">
31+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
32+
</Content>
33+
<Content Include="presets\default.json">
34+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
35+
</Content>
36+
<Content Include="presets\full-npc.json">
37+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
38+
</Content>
39+
<Content Include="presets\improved-vanilla.json">
40+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
41+
</Content>
42+
<Content Include="presets\normal-npc.json">
43+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
44+
</Content>
45+
<Content Include="presets\tournament.json">
46+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
47+
</Content>
2148
</ItemGroup>
2249

2350
<ItemGroup>
51+
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
2452
<PackageReference Include="RomUtilities" Version="2.2.0" />
2553
</ItemGroup>
2654

FF1Lib/FF1Rom.cs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace FF1Lib
1212
// ReSharper disable once InconsistentNaming
1313
public partial class FF1Rom : NesRom
1414
{
15-
public const string Version = "2.1.0";
15+
public const string Version = "2.2.0";
1616

1717
public const int RngOffset = 0x7F100;
1818
public const int RngSize = 256;
@@ -83,17 +83,20 @@ public void Randomize(Blob seed, Flags flags)
8383
var rng = new MT19337(BitConverter.ToUInt32(seed, 0));
8484

8585
UpgradeToMMC3();
86+
MakeSpaceIn1F();
8687
EasterEggs();
8788
DynamicWindowColor();
8889
PermanentCaravan();
90+
ShiftEarthOrbDown();
91+
8992
var overworldMap = new OverworldMap(this, flags);
9093
var maps = ReadMaps();
9194
var incentivesData = new IncentiveData(rng, flags, overworldMap.MapLocationRequirements);
9295
var shopItemLocation = ItemLocations.CaravanItemShop1;
93-
96+
9497
if (flags.ModernBattlefield)
9598
{
96-
SetBattleUI(true);
99+
EnableModernBattlefield();
97100
}
98101

99102
if (flags.TitansTrove)
@@ -112,6 +115,11 @@ public void Randomize(Blob seed, Flags flags)
112115
FixEnemyAOESpells();
113116
}
114117

118+
if (flags.ItemMagic)
119+
{
120+
ShuffleItemMagic(rng);
121+
}
122+
115123
if (flags.Shops)
116124
{
117125
var excludeItemsFromRandomShops = flags.Treasures
@@ -125,6 +133,16 @@ public void Randomize(Blob seed, Flags flags)
125133
ShuffleTreasures(rng, flags, incentivesData, shopItemLocation, overworldMap.MapLocationRequirements);
126134
}
127135

136+
if (flags.Treasures && flags.ShardHunt && !flags.ChaosRush)
137+
{
138+
EnableShardHunt(rng, flags.ExtraShards ? rng.Between(24, 30) : 16, maps, flags.NPCItems);
139+
}
140+
141+
if (flags.TransformFinalFormation)
142+
{
143+
TransformFinalFormation((FinalFormation)rng.Between(0, Enum.GetValues(typeof(FinalFormation)).Length - 1));
144+
}
145+
128146
if (flags.MagicShops)
129147
{
130148
ShuffleMagicShops(rng);
@@ -241,7 +259,7 @@ public void Randomize(Blob seed, Flags flags)
241259
EnableBuyTen();
242260
}
243261

244-
if (flags.EnemyFormationsUnrunnable || flags.WaitWhenUnrunnable)
262+
if (flags.WaitWhenUnrunnable)
245263
{
246264
ChangeUnrunnableRunToWait();
247265
}
@@ -276,6 +294,11 @@ public void Randomize(Blob seed, Flags flags)
276294
FixBBAbsorbBug();
277295
}
278296

297+
if (flags.ImproveTurnOrderRandomization)
298+
{
299+
ImproveTurnOrderRandomization(rng);
300+
}
301+
279302
if (flags.EnemyElementalResistancesBug)
280303
{
281304
FixEnemyElementalResistances();
@@ -440,6 +463,23 @@ private void ExtraTrackingAndInitCode()
440463
// Softlock fix
441464
Put(0x7C956, Blob.FromHex("A90F2003FE4C008B"));
442465
PutInBank(0x0F, 0x8B00, Blob.FromHex("BAE030B01E8A8D1001A9F4AAA9FBA8BD0001990001CA88E010D0F4AD1001186907AA9AA52948A52A48A50D48A54848A549484C65C9"));
466+
467+
// Change INT to MDEF in the Status screen
468+
Put(0x388F5, Blob.FromHex("968D8E8F"));
469+
Data[0x38DED] = 0x25;
470+
}
471+
472+
public void MakeSpaceIn1F()
473+
{
474+
// 54 bytes starting at 0xC265 in bank 1F, ROM offset: 7C275
475+
// This removes the code for the minigame on the ship, and moves the prior code around too
476+
PutInBank(0x1F, 0xC244, Blob.FromHex("F003C6476020C2D7A520290FD049A524F00EA9008524A542C908F074C901F0B160EAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEA"));
477+
// 15 bytes starting at 0xC8A4 in bank 1F, ROM offset: 7C8B4
478+
// This removes the routine that give a reward for beating the minigame, no need for a reward without the minigame
479+
PutInBank(0x1F, 0xC8A4, Blob.FromHex("EAEAEAEAEAEAEAEAEAEAEAEAEAEAEA"));
480+
// 28 byte starting at 0xCFCB in bank 1F, ROM offset: 7CFDB
481+
// This removes the AssertNasirCRC routine, which we were skipping anyways, no point in keeping uncalled routines
482+
PutInBank(0x1F, 0xCFCB, Blob.FromHex("EAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEA"));
443483
}
444484

445485
public override bool Validate()

FF1Lib/FF1Text.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public static Blob TextToStory(string[] lines)
122122
List<Blob> buffers = new List<Blob>();
123123
for (int i = 0; i < lines.Length; ++i)
124124
{
125-
buffers.Add(TextToBytes(lines[i], useDTE: false, delimiter: Delimiter.Line));
125+
buffers.Add(TextToBytes(lines[i], useDTE: true, delimiter: Delimiter.Line));
126126
}
127127

128128
if (buffers.Count != 0)

FF1Lib/Flags.cs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using Newtonsoft.Json;
45

56
namespace FF1Lib
67
{
@@ -39,14 +40,20 @@ public class Flags : IIncentiveFlags, IMapEditFlags
3940
public bool RandomLoot { get; set; } // Planned 2.x feature - random non-quest-item treasures
4041

4142
[FlagString(Character = ALT_GAME_MODE, FlagBit = 1)]
42-
public bool OrbHunt { get; set; } // Planned 2.1 or 2.2 features - shard hunt
43+
public bool ShardHunt { get; set; }
44+
[FlagString(Character = ALT_GAME_MODE, FlagBit = 2)]
45+
public bool ExtraShards { get; set; }
46+
[FlagString(Character = ALT_GAME_MODE, FlagBit = 4)]
47+
public bool TransformFinalFormation { get; set; }
4348

4449
[FlagString(Character = MAGIC, FlagBit = 1)]
4550
public bool MagicShops { get; set; }
4651
[FlagString(Character = MAGIC, FlagBit = 2)]
4752
public bool MagicLevels { get; set; }
4853
[FlagString(Character = MAGIC, FlagBit = 4)]
4954
public bool MagicPermissions { get; set; }
55+
[FlagString(Character = MAGIC, FlagBit = 8)]
56+
public bool ItemMagic { get; set; }
5057

5158
[FlagString(Character = ENCOUNTERS, FlagBit = 1)]
5259
public bool Rng { get; set; }
@@ -190,6 +197,8 @@ public class Flags : IIncentiveFlags, IMapEditFlags
190197
public bool EnemySpellsTargetingAllies { get; set; }
191198
[FlagString(Character = ENEMY_BUG_FIXES, FlagBit = 4)]
192199
public bool EnemyElementalResistancesBug { get; set; }
200+
[FlagString(Character = ENEMY_BUG_FIXES, FlagBit = 8)]
201+
public bool ImproveTurnOrderRandomization { get; set; }
193202

194203
[FlagString(Character = 17, Multiplier = 0.1)]
195204
public double EnemyScaleFactor { get; set; }
@@ -201,7 +210,7 @@ public class Flags : IIncentiveFlags, IMapEditFlags
201210
public int ExpBonus { get; set; }
202211
[FlagString(Character = 21, Multiplier = 1)]
203212
public int ForcedPartyMembers { get; set; }
204-
213+
205214
public bool ModernBattlefield { get; set; }
206215
public bool FunEnemyNames { get; set; }
207216
public bool PaletteSwap { get; set; }
@@ -266,11 +275,6 @@ public static Dictionary<string, FlagStringAttribute> GetFlagStringAttributes()
266275
.FirstOrDefault() as FlagStringAttribute);
267276
}
268277

269-
public string GetString()
270-
{
271-
return EncodeFlagsText(this);
272-
}
273-
274278
// ! and % are printable in FF, + and / are not.
275279
private const string base64CharString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-!";
276280
public static string EncodeFlagsText(Flags flags)
@@ -308,12 +312,14 @@ public static Flags DecodeFlagsText(string text)
308312
foreach (var inputChar in inputCharacters)
309313
{
310314
var charFlagValue = base64CharString.IndexOf(inputChar);
311-
var flagAttributesForChar = flagAttributes[index];
315+
var flagAttributesForChar = flagAttributes[index++];
312316
if (flagAttributesForChar.Any(x => x.Value.FlagBit < 1))
313317
{
314318
var multiplierAttribute = flagAttributesForChar.First(x => x.Value.FlagBit < 1);
319+
315320
var outputValue = charFlagValue * multiplierAttribute.Value.Multiplier;
316-
typeof(Flags).GetProperty(multiplierAttribute.Key).SetValue(result, outputValue);
321+
var property = typeof(Flags).GetProperty(multiplierAttribute.Key);
322+
property.SetValue(result, Convert.ChangeType(outputValue, property.PropertyType));
317323
continue;
318324
}
319325
foreach (var flagAttribute in flagAttributesForChar)
@@ -324,6 +330,14 @@ public static Flags DecodeFlagsText(string text)
324330
}
325331
return result;
326332
}
333+
334+
private class Preset
335+
{
336+
public string Name { get; set; }
337+
public Flags Flags { get; set; }
338+
}
339+
340+
public static Flags FromJson(string json) => JsonConvert.DeserializeObject<Preset>(json).Flags;
327341
}
328342

329343
public class FlagStringAttribute : Attribute
@@ -346,7 +360,6 @@ public string ToVueComputedPropertyString()
346360
$"set:function(newValue){{while(this.flagString.length <= {Character})this.flagString += base64Chars[0];" +
347361
$"var scaledValue = (newValue / {Multiplier}).toFixed() % base64Chars.length;" +
348362
$"this.flagString = this.flagString.substr(0,{Character}) + base64Chars[scaledValue] + this.flagString.substr({Character + 1});}} }}";
349-
350363
}
351364
}
352365
}

0 commit comments

Comments
 (0)