• Welcome to Final Fantasy Hacktics. Please login or sign up.
 
March 28, 2024, 05:23:27 am

News:

Use of ePSXe before 2.0 is highly discouraged. Mednafen, RetroArch, and Duckstation are recommended for playing/testing, pSX is recommended for debugging.


FFT Patcher (.497)

Started by formerdeathcorps, May 14, 2011, 03:57:55 am

formerdeathcorps

Persevere = Performing because that's what that flag does.  It changes a charged attack into a performed one.

Countergrasp is affected by the following:
Counter
Blade Grasp
Counter Tackle
Brave Up
Dragon Spirit
Sunken State
This is the information from the Battle Mechanics Guide by Aerostar.
The destruction of the will is the rape of the mind.
The dogmas of every era are nothing but the fantasies of those in power; their dreams are our waking nightmares.

RavenOfRazgriz

I would leave it as "Normal Attack?" until more thorough research is done.  It's stupid to give it a name that sounds like we /know/ what it does when we very clearly don't have the full low-down on it yet.

Continuous pr Performing is fine for Persevering.  I prefer Continuous since it can be used on things that aren't actually a performance. (Aka, aren't Songs/Dances or even remotely related to them, you can have continuously-triggered weapon or magic attacks.) I still find Channel to be the worst name, though.  I can simply reverse your example - "I channeled magic and cast my spell through my foe's attacks."  At least Persevere is a synonym for Persist, whereas Channel is very much implied to be magic-only if you're read up on nerdy magic lore at all or is otherwise worthless.  It's too narrow and implies many things it shouldn't, like that it only works on magical skills, assuming you can even figure out what it is without cross-referencing it somehow.

Countergraspable is also fine because it's basic FFT slang - if you've ever read the Battle Mechanics Guide, you'll know the term, and know it refers to whether the ability is affected by the Reactions Counter Tackle, Counter, and Blade Grasp / Shirahadori, and whether the Reaction can trigger Dragon Spirit / Dragon's Soul, Sunken State / Vanish, and Brave Up / Bravery Boost.  The term Countergrasp is taken directly from the Battle Mechanics Guide and even got its own section in said guide, posted here:

COUNTERGRASP:  So called from the two most popular reaction abilities of
this class, Counter and Blade Grasp, these attacks will be triggered by
any of the physical attacks listed below. The attack does not have to
succeed or cause damage in order for the reaction ability to activate.
Like most reaction abilities, these will activate Br% of the time.

          ------------LIST OF COUNTERGRASP ACTIVATORS-------------
         |========================================================|
         |  ATTACK*           Eye Gouge         Beaking           |
         |  JUMP              Scratch           Shine Lover       |
         |  THROW             Poison Nail       Straight Dash     |
         |  CHARGE            Blood Suck**      Bite              |
         |  BATTLE SKILL      Tentacle (squid)  Shake Off         |
         |  Steal Helmet      Knife Hand        Wave Around       |
         |  Steal Armor       Sleep Touch       Tentacle (morbol) |
         |  Steal Weapon      Grease Touch      Goo               |
         |  Steal Accessry    Drain Touch       Stab Up           |
         |  Steal Shield      Zombie Touch      Sudden Cry        |
         |  Gil Taking        Snake Carrier     Dash (dragon)     |
         |  Choco Attack      Wing Attack       Tail Swing        |
         |  Tackle            Scratch Up        Triple Attack     |
         |  Goblin Punch      Beak                                |
         |                                                        |
         |  * Countergrasp reactions (except Blade Grasp) do not  |
         |    work against ATTACKs with magic guns                |
         |  ** Vampire version of 'Blood Suck' only!              |
          --------------------------------------------------------

   [Blade Grasp] - 700 JP, Samurai  
   This ability makes the base hit percentage for Countergrasp
   activators executed against you equal to (Base - YourBr) instead of
   Base, where Base is the success rate of the attack before evasion
   is considered.  Unlike the other Countergrasp reaction abilities,
   Blade Grasp DOES work against magic guns.  Blade Grasp can only
   activate once per attack, so it will at most block one of the two
   hits from a Two Swords attack.  Blade Grasp is NOT considered to
   be evasion, and its effects stack with those of evasion. See
   section 6.6 for details.

   [Brave Up] - 500 JP, Dancer
   Your Br is increased by 3.

   [Counter] - 300 JP, Monk
   If the unit which attacked you is within range of your equipped
   weapon, you will counter with an ATTACK directive. The Counter-ATTACK
   can't trigger reactions (except pseudo-supports), which means it can't
   be blocked by Blade Grasp.

   [Counter Tackle] - 180 JP, Squire
   If the unit that attacked you is within range of 'Dash', you will
   counter with 'Dash'.

   [Dragon Spirit] - 560 JP, Lancer  
   You gain Reraise status, unless you already have Reraise or are immune
   to Reraise.

   [Sunken State] - 900 JP, Ninja
   You gain Transparent status, unless you are immune to Transparent.


If you haven't even read the Battle Mechanics Guide... I can't expect you to be able mod the gameplay aspect of FFT competently, honestly.  It contains basic info on a lot of Formula and other-related stuff that most people here will assume you know or easily know how to look up as a baseline when modding.


Eternal

Why can't we stick with Persevere? Everyone knew what it did.
  • Modding version: PSX & WotL
"You, no less human than we? Ha! Now there's a beastly thought. You've been less than we from the moment your baseborn father fell upon your mother in whatever gutter saw you sired! You've been chattel since you came into the world drenched in common blood!"
  • Discord username: eternal248#1817

formerdeathcorps

Raven, you're missing my point.  Flagging "perform" flag causes the unit to gain "performing" status.  This status is different in vanilla FFT than "charging" since you don't take 1.5x damage when "performing".  Hence, it doesn't make much sense to give that flag any other name.
The destruction of the will is the rape of the mind.
The dogmas of every era are nothing but the fantasies of those in power; their dreams are our waking nightmares.

Glain

I want to move away from Persevere because it isn't clear. I certainly didn't know what it meant.

If everyone knew what it did, then it was because everyone just memorized it, and in that case, it doesn't matter what the label is anyway. It might as well have been called Flag 22 or something.

We can't make any progress if we shy away from changing things because people memorized the old ways.

EDIT: For the record, I like "Performing" because of what fdc is saying.

Also, is the "Countergrasp" term really well known? Not sure how many people have really read the BMG. I'm okay with it if so. Counter/Grasp maybe, but it still doesn't show everything. I'll try adding a tooltip to it maybe to show all the things it affects.
  • Modding version: Other/Unknown

Pickle Girl Fanboy

Does countergraspable refer to "When flagged, this ability triggers reactions dependent on HP damage"?  And, if anyone cares about my opinion, then FDC is right, it should be "Performing".

RavenOfRazgriz

Quote from: Pickle Girl Fanboy on January 14, 2012, 05:40:09 pm
Does countergraspable refer to "When flagged, this ability triggers reactions dependent on HP damage"?


Look at the spoiler in this post, PGF.  You obviously read this post once since you replied to my response to Glain...



Performing is fine.  I wasn't saying it was wrong - it's right for the reasons FDC listed.  I just prefer "Continuous" because it shows the skill is continuous, and that the flag works on anything, and not just a "performance" skill like Singing or Dancing.  Yes that's a weird way to look at people but that's how your average shmoe who knows nothing about code could easily see it.  I'm fine with either label though, really.

Like I said, Glain, modding without reading the BMG sounds like a terrible idea.  If you don't know how things work, how can you mod it?  If you can add a tooltip, though, that'd be fine.  Just name it CounterGraspable and have a tooltip pop up if you hover over the name for a minute or something, I dunno.  The only one Reaction I ever forget is Countergraspable is Brave Up though, myself, simply because I always expect it to be in parity with Faith Up.  (And hence, my request to you in your ASM thread.)

Pickle Girl Fanboy

Quote from: RavenOfRazgriz on January 14, 2012, 05:48:13 pm
Look at the spoiler in this post, PGF.  You obviously read this post once since you replied to my response to Glain...

I was surprised by that, and I wanted verification, but I decided to be a little sneaky about it.  I thought the "bonus" reactions were separate from the "damage-enemies" and the "evade-attacks".

RavenOfRazgriz

They're not.  What the Reaction does is irrelevant to the trigger.  There's several triggers:

Counter Grasp (Counter, Blade Grasp, etc)
Counter Flood
Counter Magic
HP Damage (Caution, Regenerator, PA Save, MA Save, some others)
Affected by Attack/Skill (Faith Up, maybe others)
Affected by Specific Formula (Catch, Finger Guard)

[Uncertain] Weapon Ranged Skill (Hamedo)

Then there's Distribute and maybe one or two others I missed with very weird and unique trigger conditions.  As you can see, what the skill does really isn't related to how it triggers.  There is no "Adds Status to Self" grouping or anything like that.

Pickle Girl Fanboy

My last off-topic post: I bet the asm for triggers, and R/S/M's in general, is royally screwed up.

RavenOfRazgriz

Hey Glain, you're the cool guy whose able to get .478 compiling correctly.  In FFTacText, it has a QuickEdit function that edits areas that appear many times over in files... but it's missing some key areas like Unit Name, Help Window editors, etc.  If I can assemble the titles of those windows and all the different files they appear in, would it be possible for you (or FDC, but he's busy enough with the ASM shit) to add QuickEdit options to FFTacText for these missing things so that trying to edit things like the tooltip that comes up when you press Select on a unit's Party Roster Number or the Unit Name List and stuff without it taking years.

Glain

January 14, 2012, 11:58:16 pm #52 Last Edit: January 15, 2012, 11:13:47 am by Glain
I'm sure it's possible... but I'll have to figure out how QuickEdit works first. I'll take a look at that while I make the edits to the flag names, etc., and see what I can do. It doesn't hurt to make the list regardless, because I'm sure I'll figure it out eventually.

EDIT: For flag names, I'm going with "Normal Attack?", "Performing", and "Countergrasp" and just hope people read the BMG. Can't do Countergraspable; it's inconsistent with things like "Counter Magic" and "Counter Flood" and I can't get those to sound right no matter what I do ("Affected by Counter Magic" is too long, etc, "Counter Magicable" is just... what?).

EDIT: I've also got ability IDs preceding ability names, like "015E Grand Cross"
  • Modding version: Other/Unknown

RavenOfRazgriz

Quote from: Glain on January 14, 2012, 11:58:16 pmEDIT: I've also got ability IDs preceding ability names, like "015E Grand Cross"


Means redoing my Resources.zip for Abilities, but damn that's more useful than you realize.  (When Event Editing, you play an Effect by calling its Ability ID, not its Animation ID.  Having the Ability ID right there to look at when sifting through your skills for the one you want to play will help so much when Event Editing.)

Those flag names sound fine, too.  I'll try to get that QuickEdit list stuff to you tonight.  I've been busy doing a lot of FFTPatcher work for Journey of the Five, but I'm switching over to doing Spell Quotes tonight so I'll look up all the stuff you'll need to update the QuickEdit list before I get started on those.

RavenOfRazgriz

January 20, 2012, 01:56:18 am #54 Last Edit: January 26, 2012, 09:31:36 pm by RavenOfRazgriz
I'm posting that stuff about FFTacText now.  I'm going to put the name of the field in quotation marks, then list every file the window is listed under.  This will also include every file only listed in one window, since honestly, QuickEdit should allow you to edit all the text in the game without jumping between files hunting for shit.  Whether you feel that way as well or not, I do not know, but everything will be here regardless.


ATTACK.OUT
OPEN.LZW
WORLD.LZW
WORLD.LZW (appears twice)



WLDHELP.LZW



REQUIRE.OUT



REQUIRE.OUT



REQUIRE.OUT
WORLD.BIN 05



ATCHELP.LZW
BUNIT.OUT
HELP.LZW
WLDHELP.LZW



EQUIP.OUT
HELP.LZW
WLDHELP.LZW



BATTLE.BIN
CARD2.OUT
WLDHELP.LZW
WORLD.BIN 04



BATTLE.BIN



HELP.LZW



HELP.LZW



BATTLE.BIN
WORLD.LZW



BATTLE.BIN



BATTLE.BIN



BATTLE.BIN



BATTLE.BIN



BUNIT.OUT



CARD1.OUT



OPEN.LZW



OPEN.LZW



OPEN.LZW



SNPLMES.BIN



TUTO1.MES to TUTO7.MES



WORLD.LZW



WLDHELP.LZW



WORLD.LZW



WORLD.LZW



WLDHELP.LZW
WORLD.BIN 09



WLDMES.BIN
WORLD.LZW (Facts only)



WORLD.BIN 08



WORLD.BIN 10



WORLD.LZW



WORLD.LZW



WORLD.LZW



WORLD.LZW



WORLD.LZW



WORLD.LZW


There you go.  Personally, I feel that the Text dropdown should BE the QuickEdit menu, and all this stuff should be what you get to choose from instead.  Since there's a lot of items, maybe group them into several obvious and intuitive quickedit groups, so you pick a group, then find what you need to edit off that list.  Either way, this is all the info, do with it as you will.


Also, for .479 FFTPatcher, can you please list the Item ID of an Item/Weapon/etc next to an Item/Weapon/etc in the same way you listed Ability IDs?  Having that info at a glance would be amazing for Event Editing, since you need to know Item IDs to assign Temporary Weapons.  It's on the Wiki, but it's stupid to have to go a million places for things that should all be contained in one place, and the Wiki's much harder to make use of if you're changing lots of weapon names and such.

Glain

I've got item IDs in there now.

I've spent a while trying to figure out why it was generating Gameshark codes for Propositions even when nothing was changed. There's this concept of "buggy level bonuses" that keeps being referenced, and a bit value to say whether or not the level bonuses are buggy in the ISO? It was set to true, which made the values different from the ones it thought of as the defaults, so it generated the Gameshark codes to change them; setting the flag to false makes the codes disappear.

It just seems weird. Am I missing something by just setting that flag to false and being done with it? The code's got a bunch of branching decisions based on whether or not that "buggy level bonuses" flag is set and I have no idea what it's all about.
  • Modding version: Other/Unknown


Glain

Er... I pretty well have to post the whole document it's in; it's sprinkled throughout. If you really want it:



using System;
using System.Collections.Generic;
using System.Text;
using PatcherLib.Utilities;
using PatcherLib.Datatypes;
using PatcherLib;
using Lokad;

namespace FFTPatcher.Datatypes
{
   public enum PropositionClass
   {
       Squire = 0,
       Chemist,
       Knight,
       Archer,
       Monk,
       Priest,
       Wizard,
       TimeMage,
       Summoner,
       Thief,
       Mediator,
       Oracle,
       Geomancer,
       Lancer,
       Samurai,
       Ninja,
       Calculator,
       Bard,
       Dancer,
       Mime,
       Specials,
       Monsters
   }

   public enum RandomSuccessClass
   {
       None = 0,
       Squire,
       Chemist,
       Knight,
       Archer,
       Monk,
       Priest,
       Wizard,
       TimeMage,
       Summoner,
       Thief,
       Mediator,
       Oracle,
       Geomancer,
       Lancer,
       Samurai,
       Ninja,
       Calculator,
       Bard,
       Dancer,
       Mime,
   }

   public enum BraveFaithRange
   {
       _1to20 = 0,
       _21to40,
       _41to60,
       _61to80,
       _81to100
   }

   public enum LevelRange
   {
       _1to10 = 0,
       _11to20,
       _21to30,
       _31to40,
       _41to50,
       _51to60,
       _61to70,
       _71to80,
       _81to90,
       _91to100,
   }

   public enum BonusPercent
   {
       _50Percent = 0,
       _40Percent,
       _10Percent
   }

   public enum BonusIndex
   {
       Nothing = 0,
       _One,
       _Two,
       _Three,
       _Four,
       _Five,
       _Six,
       _Seven,
       _Eight,
   }
   public class AllPropositions : PatchableFile, IXmlDigest, IGenerateCodes
   {
       public const int NumPropositions = 96;
       public const int propLength = 23;

       public AllPropositions( IList<byte> bytes, bool brokenLevelBonuses )
           : this( bytes, null as AllPropositions, brokenLevelBonuses )
       {
       }

       public AllPropositions( IList<byte> bytes, IList<byte> defaultBytes, bool brokenLevelBonuses )
           : this( bytes, new AllPropositions( defaultBytes, true ), brokenLevelBonuses )
       {
       }

       public bool MirrorLevelRanges { get { return !CanFixBuggyLevelBonuses( FFTPatch.Context ); } }

       public IList<UInt16> Prices { get; private set; }
       public IList<UInt16> SmallBonuses { get; private set; }
       public IList<UInt16> LargeBonuses { get; private set; }
       public IList<UInt16> JPMultipliers { get; private set; }
       public IList<UInt16> GilMultipliers { get; private set; }

       private TupleDictionary<PropositionType, PropositionClass, byte> propTypeBonuses;
       public TupleDictionary<PropositionType, PropositionClass, byte> PropositionTypeBonuses { get { return propTypeBonuses; } }

       private TupleDictionary<BraveFaithNeutral, PropositionClass, byte> bfBonuses;
       public TupleDictionary<BraveFaithNeutral, PropositionClass, byte> BraveFaithBonuses { get { return bfBonuses; } }

       private TupleDictionary<BraveFaithNeutral, BraveFaithRange, byte> braveBonuses;
       public TupleDictionary<BraveFaithNeutral, BraveFaithRange, byte> BraveStatBonuses { get { return braveBonuses; } }

       private TupleDictionary<BraveFaithNeutral, BraveFaithRange, byte> faithBonuses;
       public TupleDictionary<BraveFaithNeutral, BraveFaithRange, byte> FaithStatBonuses { get { return faithBonuses; } }

       private TupleDictionary<BraveFaithNeutral, LevelRange, byte> levelBonuses;
       public TupleDictionary<BraveFaithNeutral, LevelRange, byte> LevelBonuses { get { return levelBonuses; } }

       private IDictionary<PropositionType, BonusIndex> treasureLandJpBonuses;
       public IDictionary<PropositionType, BonusIndex> TreasureLandJpBonuses { get { return treasureLandJpBonuses; } }

       private IDictionary<PropositionType, BonusIndex> treasureLandGilBonuses;
       public IDictionary<PropositionType, BonusIndex> TreasureLandGilBonuses { get { return treasureLandGilBonuses; } }

       private TupleDictionary<PropositionType, BonusPercent, BonusIndex> bonusCashGilBonuses;
       public TupleDictionary<PropositionType, BonusPercent, BonusIndex> BonusCashGilBonuses { get { return bonusCashGilBonuses; } }

       private TupleDictionary<PropositionType, BonusPercent, BonusIndex> bonusCashJpBonuses;
       public TupleDictionary<PropositionType, BonusPercent, BonusIndex> BonusCashJpBonuses { get { return bonusCashJpBonuses; } }

       IList<byte> realLevelBonusBytes;

       public void SetPropositionTypeBonus( PropositionType type, PropositionClass _class, byte value )
       {
           propTypeBonuses[type, _class] = value;
       }

       public void SetBraveFaithBonus( BraveFaithNeutral bfn, PropositionClass _class, byte value )
       {
           bfBonuses[bfn, _class] = value;
       }

       public AllPropositions Default { get; private set; }

       public AllPropositions( IList<byte> bytes, AllPropositions defaults, bool brokenLevelBonuses )
       {
           Default = defaults;
           var names = FFTPatch.Context == Context.US_PSP ? PSPResources.Lists.Propositions : PSXResources.Lists.Propositions;

           List<Proposition> props = new List<Proposition>();
           if (defaults != null)
           {
               for (int i = 0; i < NumPropositions; i++)
               {
                   props.Add( new Proposition( names[i], bytes.Sub( i * propLength, (i + 1) * propLength - 1 ), defaults.Propositions[i] ) );
               }
           }
           else
           {
               for (int i = 0; i < NumPropositions; i++)
               {
                   props.Add( new Proposition( names[i], bytes.Sub( i * propLength, (i + 1) * propLength - 1 ) ) );
               }
           }

           Prices = new UInt16[8];
           for (int i = 0; i < 8; i++)
           {
               var b = bytes.Sub( propLength * NumPropositions + i * 2 + 2, propLength * NumPropositions + i * 2 + 2 + 1 );
               Prices[i] = Utilities.BytesToUShort( b[0], b[1] );
           }

           unknownBytes = bytes.Sub( 0x8b2, 0x8bf ).ToArray();

           propTypeBonuses = new TupleDictionary<PropositionType, PropositionClass, byte>();

           foreach (PropositionType type in (PropositionType[])Enum.GetValues( typeof( PropositionType ) ))
           {
               foreach (PropositionClass _class in (PropositionClass[])Enum.GetValues( typeof( PropositionClass ) ))
               {
                   propTypeBonuses[type, _class] = bytes[0x8C0 + ((int)type - 1) * 22 + (int)_class];
               }
           }
           propTypeBonuses = new TupleDictionary<PropositionType, PropositionClass, byte>( propTypeBonuses, false, true );

           bfBonuses = new TupleDictionary<BraveFaithNeutral, PropositionClass, byte>();
           foreach (BraveFaithNeutral bfn in (BraveFaithNeutral[])Enum.GetValues( typeof( BraveFaithNeutral ) ))
           {
               foreach (PropositionClass _class in (PropositionClass[])Enum.GetValues( typeof( PropositionClass ) ))
               {
                   bfBonuses[bfn, _class] = bytes[0x970 + ((int)bfn - 1) * 22 + (int)_class];
               }
           }
           bfBonuses = new TupleDictionary<BraveFaithNeutral, PropositionClass, byte>( bfBonuses, false, true );

           braveBonuses = new TupleDictionary<BraveFaithNeutral, BraveFaithRange, byte>();
           faithBonuses = new TupleDictionary<BraveFaithNeutral, BraveFaithRange, byte>();
           levelBonuses = new TupleDictionary<BraveFaithNeutral, LevelRange, byte>();

           int levelBonusesOffset = brokenLevelBonuses ? 0x9B4 : 0x9D4;
           foreach (BraveFaithNeutral bfn in (BraveFaithNeutral[])Enum.GetValues( typeof( BraveFaithNeutral ) ))
           {
               foreach (BraveFaithRange range in (BraveFaithRange[])Enum.GetValues( typeof( BraveFaithRange ) ))
               {
                   braveBonuses[bfn, range] = bytes[0x9B4 + ((int)bfn - 1) * 5 + (int)range];
                   faithBonuses[bfn, range] = bytes[0x9C4 + ((int)bfn - 1) * 5 + (int)range];
               }

               foreach (LevelRange range in (LevelRange[])Enum.GetValues( typeof( LevelRange ) ))
               {
                   levelBonuses[bfn, range] = bytes[levelBonusesOffset + ((int)bfn - 1) * 10 + (int)range];
               }
           }


           braveBonuses = new TupleDictionary<BraveFaithNeutral, BraveFaithRange, byte>( braveBonuses, false, true );
           faithBonuses = new TupleDictionary<BraveFaithNeutral, BraveFaithRange, byte>( faithBonuses, false, true );
           levelBonuses = new TupleDictionary<BraveFaithNeutral, LevelRange, byte>( levelBonuses, false, true );

           treasureLandJpBonuses = new Dictionary<PropositionType, BonusIndex>();
           treasureLandGilBonuses = new Dictionary<PropositionType, BonusIndex>();
           bonusCashGilBonuses = new TupleDictionary<PropositionType, BonusPercent, BonusIndex>();
           bonusCashJpBonuses = new TupleDictionary<PropositionType, BonusPercent, BonusIndex>();

           foreach (PropositionType type in (PropositionType[])Enum.GetValues( typeof( PropositionType ) ))
           {
               treasureLandGilBonuses[type] = (BonusIndex)bytes[0x9F4 + 2 * ((int)type - 1)];
               treasureLandJpBonuses[type] = (BonusIndex)bytes[0x9F4 + 2 * ((int)type - 1) + 1];

               bonusCashGilBonuses[type, BonusPercent._10Percent] = (BonusIndex)bytes[0xA04 + ((int)type - 1) * 6 + 2 * 2];
               bonusCashGilBonuses[type, BonusPercent._40Percent] = (BonusIndex)bytes[0xA04 + ((int)type - 1) * 6 + 2 * 1];
               bonusCashGilBonuses[type, BonusPercent._50Percent] = (BonusIndex)bytes[0xA04 + ((int)type - 1) * 6 + 2 * 0];

               bonusCashJpBonuses[type, BonusPercent._10Percent] = (BonusIndex)bytes[0xA04 + ((int)type - 1) * 6 + 2 * 2 + 1];
               bonusCashJpBonuses[type, BonusPercent._40Percent] = (BonusIndex)bytes[0xA04 + ((int)type - 1) * 6 + 2 * 1 + 1];
               bonusCashJpBonuses[type, BonusPercent._50Percent] = (BonusIndex)bytes[0xA04 + ((int)type - 1) * 6 + 2 * 0 + 1];
           }
           bonusCashGilBonuses = new TupleDictionary<PropositionType, BonusPercent, BonusIndex>( bonusCashGilBonuses, false, true );
           bonusCashJpBonuses = new TupleDictionary<PropositionType, BonusPercent, BonusIndex>( bonusCashJpBonuses, false, true );

           SmallBonuses = new UInt16[8];
           LargeBonuses = new UInt16[8];
           for (int i = 0; i < 8; i++)
           {
               SmallBonuses[i] = Utilities.BytesToUShort( bytes[0xA34 + i * 2], bytes[0xA34 + i * 2 + 1] );
               LargeBonuses[i] = Utilities.BytesToUShort( bytes[0xA44 + i * 2], bytes[0xA44 + i * 2 + 1] );
           }

           JPMultipliers = new UInt16[10];
           GilMultipliers = new UInt16[10];

           for (int i = 0; i < 10; i++)
           {
               JPMultipliers[i] = Utilities.BytesToUShort( bytes[0xA54 + i * 2], bytes[0xA54 + i * 2 + 1] );
               GilMultipliers[i] = Utilities.BytesToUShort( bytes[0xA68 + i * 2], bytes[0xA68 + i * 2 + 1] );
           }


           Propositions = props.AsReadOnly();
       }

       private IList<byte> unknownBytes;

       public IList<Proposition> Propositions
       {
           get;
           private set;
       }

       public string GetCodeHeader( PatcherLib.Datatypes.Context context )
       {
           return context == Context.US_PSP ? "_C0 Propositions" : "\"Propositions";

       }

       public IList<string> GenerateCodes( PatcherLib.Datatypes.Context context )
       {
           if (context == Context.US_PSP)
           {
               return Codes.GenerateCodes( Context.US_PSP, PSPResources.Binaries.Propositions, this.ToByteArray(), 0x2E9634 );
           }
           else
           {
               return Codes.GenerateCodes( Context.US_PSX, PSXResources.Binaries.Propositions, this.ToByteArray(), 0x9D380, Codes.CodeEnabledOnlyWhen.World );
           }
       }

       public void WriteXmlDigest( System.Xml.XmlWriter writer )
       {
           throw new NotImplementedException();
       }

       public byte[] ToByteArray()
       {
           List<byte> result = new List<byte>( 0xA7C );
           Propositions.ForEach( p => result.AddRange( p.ToByteArray() ) );
           result.Add( 0x00 );
           result.Add( 0x00 );
           Prices.ForEach( p => result.AddRange( p.ToBytes() ) );
           result.AddRange( unknownBytes );

           PropositionClass[] classes = (PropositionClass[])Enum.GetValues( typeof( PropositionClass ) );
           PropositionType[] propTypes = (PropositionType[])Enum.GetValues( typeof( PropositionType ) );
           foreach (PropositionType type in propTypes)
           {
               foreach (PropositionClass _class in classes)
               {
                   result.Add( propTypeBonuses[type, _class] );
               }
           }
           BraveFaithNeutral[] bfnVals = (BraveFaithNeutral[])Enum.GetValues( typeof( BraveFaithNeutral ) );
           foreach (BraveFaithNeutral bfn in bfnVals)
           {
               foreach (PropositionClass _class in classes)
               {
                   result.Add( bfBonuses[bfn, _class] );
               }
           }

           result.Add( 0x00 );
           result.Add( 0x00 );
           BraveFaithRange[] bfnRanges = (BraveFaithRange[])Enum.GetValues( typeof( BraveFaithRange ) );

           foreach (BraveFaithNeutral bfn in bfnVals)
           {
               foreach (BraveFaithRange range in bfnRanges)
               {
                   result.Add( braveBonuses[bfn, range] );
               }
           }

           result.Add( 0x00 );

           foreach (BraveFaithNeutral bfn in bfnVals)
           {
               foreach (BraveFaithRange range in bfnRanges)
               {
                   result.Add( faithBonuses[bfn, range] );
               }
           }

           result.Add( 0x00 );

           LevelRange[] levelRanges = (LevelRange[])Enum.GetValues( typeof( LevelRange ) );
           foreach (BraveFaithNeutral bfn in bfnVals)
           {
               foreach (LevelRange range in levelRanges)
               {
                   result.Add( levelBonuses[bfn, range] );
               }
           }

           result.Add( 0x00 );
           result.Add( 0x00 );

           foreach (PropositionType type in propTypes)
           {
               result.Add( (byte)treasureLandGilBonuses[type] );
               result.Add( (byte)treasureLandJpBonuses[type] );
           }

           foreach (PropositionType type in propTypes)
           {
               for (int i = 0; i < 3; i++)
               {
                   result.Add( (byte)bonusCashGilBonuses[type, (BonusPercent)i] );
                   result.Add( (byte)bonusCashJpBonuses[type, (BonusPercent)i] );
               }
           }

           SmallBonuses.ForEach( b => result.AddRange( b.ToBytes() ) );
           LargeBonuses.ForEach( b => result.AddRange( b.ToBytes() ) );

           JPMultipliers.ForEach( b => result.AddRange( b.ToBytes() ) );
           GilMultipliers.ForEach( b => result.AddRange( b.ToBytes() ) );

           System.Diagnostics.Debug.Assert( result.Count == 0xA7C );
           return result.ToArray();
       }

       public override IList<PatchedByteArray> GetPatches( PatcherLib.Datatypes.Context context )
       {
           var result = new List<PatchedByteArray>( 2 );
           var bytes = ToByteArray();
           if (context == Context.US_PSX)
           {
               result.Add( PatcherLib.Iso.PsxIso.Propositions.GetPatchedByteArray( bytes ) );
               foreach (var good in goodPsxInstructions)
               {
                   result.Add( new PatchedByteArray( PatcherLib.Iso.PsxIso.Sectors.WORLD_WLDCORE_BIN,
                       good.Item1, good.Item2.ToArray() ) );
               }
           }
           else if (context == Context.US_PSP)
           {
               PatcherLib.Iso.PspIso.Propositions.ForEach( kp => result.Add( kp.GetPatchedByteArray( bytes ) ) );
           }

           return result;
       }

       public override bool HasChanged
       {
           get
           {
               return
                   Default != null &&
                   (!Utilities.CompareArrays( Prices, Default.Prices ) ||
                    !Utilities.CompareArrays( SmallBonuses, Default.SmallBonuses ) ||
                    !Utilities.CompareArrays( LargeBonuses, Default.LargeBonuses ) ||
                    !Utilities.CompareArrays(JPMultipliers, Default.JPMultipliers)||
                    !Utilities.CompareArrays(GilMultipliers, Default.GilMultipliers)||
                    !propTypeBonuses.Keys.TrueForAll( k => propTypeBonuses[k] == Default.propTypeBonuses[k] ) ||
                    !bfBonuses.Keys.TrueForAll( k => bfBonuses[k] == Default.bfBonuses[k] ) ||
                    !braveBonuses.Keys.TrueForAll( k => braveBonuses[k] == Default.braveBonuses[k] ) ||
                    !faithBonuses.Keys.TrueForAll( k => faithBonuses[k] == Default.faithBonuses[k] ) ||
                    !levelBonuses.Keys.TrueForAll( k => levelBonuses[k] == Default.levelBonuses[k] ) ||
                    !treasureLandJpBonuses.Keys.TrueForAll( k => treasureLandJpBonuses[k] == Default.treasureLandJpBonuses[k] ) ||
                    !treasureLandGilBonuses.Keys.TrueForAll( k => treasureLandGilBonuses[k] == Default.treasureLandGilBonuses[k] ) ||
                    !bonusCashGilBonuses.Keys.TrueForAll( k => bonusCashGilBonuses[k] == Default.bonusCashGilBonuses[k] ) ||
                    !bonusCashJpBonuses.Keys.TrueForAll( k => bonusCashJpBonuses[k] == Default.bonusCashJpBonuses[k] ) ||
                   !Propositions.TrueForAll( p => !p.HasChanged ));
           }
       }

       private static readonly IList<Tuple<int, IList<byte>>> badPsxInstructions = new List<Tuple<int, IList<byte>>> {
           Tuple.From(0x128C0, (IList<byte>)(new byte[] { 0x21, 0xB0, 0x40, 0x00 }.AsReadOnly())), // 0x128C0 move $s6, $v0
           Tuple.From(0x12A64, (IList<byte>)(new byte[] { 0x00, 0x00, 0x00, 0x00 }.AsReadOnly())), // 0x12A64 nop
           Tuple.From(0x12A74, (IList<byte>)(new byte[] { 0x21, 0x10, 0x56, 0x00 }.AsReadOnly())), // 0x12A74 addu $v0,$s6
           Tuple.From(0x1287C, (IList<byte>)(new byte[] { 0x00, 0x00, 0x00, 0x00 }.AsReadOnly())), // 0x1287C nop
           Tuple.From(0x128C8, (IList<byte>)(new byte[] { 0x00, 0x00, 0x00, 0x00 }.AsReadOnly())), // 0x128C8 nop
       }.AsReadOnly();

       private static readonly IList<Tuple<int, IList<byte>>> goodPsxInstructions = new List<Tuple<int, IList<byte>>> {
           Tuple.From(0x128C0, (IList<byte>)(new byte[] { 0x3C, 0x00, 0xA2, 0xAF }.AsReadOnly())), // 0x128C0 sw $v0, 0x68+var_2C($sp)
           Tuple.From(0x12A64, (IList<byte>)(new byte[] { 0x3C, 0x00, 0xA5, 0x8F }.AsReadOnly())), // 0x12A64 lw $a1, 0x68+var_2C($sp)
           Tuple.From(0x12A74, (IList<byte>)(new byte[] { 0x21, 0x10, 0x45, 0x00 }.AsReadOnly())), // 0x12A74 addu $v0,$a1
           Tuple.From(0x1287C, (IList<byte>)(new byte[] { 0x0A, 0x80, 0x16, 0x3C }.AsReadOnly())), // 0x1287C lui $s6, 0x800a
           Tuple.From(0x128C8, (IList<byte>)(new byte[] { 0x54, 0xDD, 0xD6, 0x26 }.AsReadOnly())), // 0x128C8 addiu $s6, 0xDD54
       }.AsReadOnly();

       public static bool CanFixBuggyLevelBonuses( Context context )
       {
           return context == Context.US_PSX;
       }

       public static bool IsoHasBuggyLevelBonuses( System.IO.Stream iso, Context context )
       {
           if (context == Context.US_PSP)
           {
               return true;
           }
           else
           {
               bool allBad = true;
               foreach (var badInstruction in badPsxInstructions)
               {
                   var kp = new PatcherLib.Iso.PsxIso.KnownPosition( PatcherLib.Iso.PsxIso.Sectors.WORLD_WLDCORE_BIN, badInstruction.Item1, 4 );
                   var bytes = kp.ReadIso( iso );
                   allBad &= Utilities.CompareArrays( bytes, badInstruction.Item2 );
               }

               if (allBad)
                   return true;

               bool allGood = true;
               foreach (var goodInstruction in goodPsxInstructions)
               {
                   var kp = new PatcherLib.Iso.PsxIso.KnownPosition( PatcherLib.Iso.PsxIso.Sectors.WORLD_WLDCORE_BIN, goodInstruction.Item1, 4 );
                   var bytes = kp.ReadIso( iso );
                   allGood &= Utilities.CompareArrays( bytes, goodInstruction.Item2 );
               }

               if (allGood)
                   return false;

               return true;
           }
       }
   }
}



Sometimes it's referred to as brokenLevelBonuses. Note this line:


int levelBonusesOffset = brokenLevelBonuses ? 0x9B4 : 0x9D4;


So if brokenLevelBonuses is true then it looks at the wrong offset (0x9B4) instead of the right one (0x9D4). I don't get it.

I changed a true to a false in FFTPatch.cs to eliminate the unnecessary Gameshark codes generated (to this):


Propositions = new AllPropositions( PSXResources.Binaries.Propositions, PSXResources.Binaries.Propositions, false );
  • Modding version: Other/Unknown

RavenOfRazgriz

I'd just switch it to false and call it a day, myself.  It's highly likely that it was supposed to be false and melonhead derped it.

Also fixed that derpy typo in my previous post.  Good to hear that you have Item IDs in there now, though.  Hopefully you come through on fixing up FFTacText to be less of a piece of shit, too.  Would make life amazing.

RavenOfRazgriz

Looking at the Status section now.

"Status Ignored if Mount" doesn't always display the full name.  Shorten it to "Mounting Ignores".  Same with Cancelled by Immortal, shorten it to "Immortal Cancels".  Reduce Target Priority could also trim a character and just be "Lower Target Priority?".  That last one's a bit OCD, but when I click it, a few chars off the end disappear, so shortening it as much as possible is still important.

Also, FDC, I thought you'd figured out a few more flags than this?  Huh.