Author Topic: Simple way to map enemies to the techs they use?  (Read 13146 times)

Redslash

  • Earthbound (+15)
  • *
  • Posts: 21
    • View Profile
    • Twitch.tv/Redslash
Re: Simple way to map enemies to the techs they use?
« Reply #30 on: July 07, 2015, 06:58:16 pm »
Hah, my bad. I saw Phase:04 in my notes and thought "Oh my god, an 04 (random) with nothing after it." So it is complete. If it were a 1v1 with Frog, this Magus would basically never use magic then, if you still had the Masamune...

The names on that page seemed to all be straight from the ROM (but not those after FA?). I'm curious about a couple unnamed enemies which might be used somewhere, I just could not find them. 0B has a Curebeam, 3C has an attack. I identified the savepoint enemies in Magus' Castle (64), the switch enemy in the Ozzie fights (F1), and the rest were various boss parts.
« Last Edit: July 07, 2015, 07:39:36 pm by Redslash »

Mauron

  • Guru of Reason Emeritus
  • Errare Explorer (+1500)
  • *
  • Posts: 1774
  • Nu-chan
    • View Profile
    • Maurtopia
Re: Simple way to map enemies to the techs they use?
« Reply #31 on: July 08, 2015, 12:18:01 am »
0B is unused. It has the graphics of of the final Lavos's "body", but no AI.

3C appears to also be unused. It's graphics are of a Lavos shell support monster, but it only has 1 HP, and I'm not seeing it in the events for the Lavos shell location.

Redslash

  • Earthbound (+15)
  • *
  • Posts: 21
    • View Profile
    • Twitch.tv/Redslash
Re: Simple way to map enemies to the techs they use?
« Reply #32 on: July 08, 2015, 01:51:11 am »
Ah whoops, it was 3C who had Curebeam, and 82 with the attack... Curebeam guy was much more mysterious though. Thanks for checking that out.

Mauron

  • Guru of Reason Emeritus
  • Errare Explorer (+1500)
  • *
  • Posts: 1774
  • Nu-chan
    • View Profile
    • Maurtopia
Re: Simple way to map enemies to the techs they use?
« Reply #33 on: July 08, 2015, 03:32:37 am »
82 is a Nu. Its stats are identical to Nu 00, except for magic, which is 1 instead of 10, and it provides no rewards.

The most significant difference between the two is that 82 uses attack 1 instead of attack 2, so it doesn't have the same random 1 or all but 1 damage effect.

Mauron

  • Guru of Reason Emeritus
  • Errare Explorer (+1500)
  • *
  • Posts: 1774
  • Nu-chan
    • View Profile
    • Maurtopia
Re: Simple way to map enemies to the techs they use?
« Reply #34 on: July 10, 2015, 06:06:32 am »
I've updated the DataCrystal page to include a full listing of targeting subroutines. I'll update the conditions and actions sometime tomorrow.

Mauron

  • Guru of Reason Emeritus
  • Errare Explorer (+1500)
  • *
  • Posts: 1774
  • Nu-chan
    • View Profile
    • Maurtopia
Re: Simple way to map enemies to the techs they use?
« Reply #35 on: July 19, 2015, 01:28:59 am »
Two things I've discovered:

- An AI condition can be made of one or two conditions. If you add a third, it will be skipped.
- If an AI action finds no targets, it does not advance and the same action is repeated next time. This means that for actions 03, 06, 08, and 0E (unused), the AI will never advance past them.

sqykly

  • Earthbound (+15)
  • *
  • Posts: 17
    • View Profile
Re: Simple way to map enemies to the techs they use?
« Reply #36 on: November 19, 2015, 08:36:36 am »
Oy mauron, this is going to sound pretty goofy, but if that disassembly I posted seems less complete than advertised, it's due to a really catastrophic crash-related data loss that I  completely forgot about for months .  Thank you, ambien, shoddy hard drives, and open office backward compatibility!  Sadly this also affected my dump of techs (to a relatively minor degree) and the script I used to dump it in the first place.  Long story short, I find myself very much wanting plain-text, byte-by-byte data structures and union tags for just about everything.  The tech-related stuff I have mostly retained and/or can put back together with what I have, but the npc stats and ai scripts would be absolutely priceless.  Is there any chance I can scrape the source to those editors, or get offset listings for the data they're accessing e.g. byte foo enemy hp, byte bar bit baz can't be thrown, etc?  The editors are really slick, but sometimes you need something you can copy-paste, automated-analyze,  scrape, convert to json or xml - plain text stuff =D.  If it will be a big hassle, don't worry about it, just thinking it's probably laying around somewhere if it's in the editor.

Mauron

  • Guru of Reason Emeritus
  • Errare Explorer (+1500)
  • *
  • Posts: 1774
  • Nu-chan
    • View Profile
    • Maurtopia
Re: Simple way to map enemies to the techs they use?
« Reply #37 on: November 21, 2015, 04:45:54 am »
My programs aren't that fancy - what I have basically copies the data from a byte array to the various form elements on load, then back to a byte array on save.

Most of what I have comes from Geiger's Chrono Trigger Database. I'll try to bump up the updating of that at some point - Note to self, email Geiger about how to record some of the offsets - but it won't be until after the week of Thanksgiving.

sqykly

  • Earthbound (+15)
  • *
  • Posts: 17
    • View Profile
Re: Simple way to map enemies to the techs they use?
« Reply #38 on: November 21, 2015, 04:48:43 am »
Here you can see #FB's AI script:

http://datacrystal.romhacking.net/wiki/Chrono_Trigger/Enemy_AI_Documentation

The name Magus may be misapplied (dunno who attached the names), but the script is definately Magus-like since it spams the big 3 and has specific Masamune react-scripts, but not against Masamune II. It only uses physical attacks on its turn, and counters with full-screen elemental mayhem.

I noticed how enemy HP-draining attacks seem to scale with character's level. Are they percentage-based?

I got this one!  Yes.  The damage caused by drain attacks is encoded as a fraction of maximum hp or mp, in 1/20th increments.  So for a drain tech of power p against a target with maximum hp m , minimum damage is d = m × p ÷ 20 with a random 0 to (d ÷ 10) + 3 added. 

sqykly

  • Earthbound (+15)
  • *
  • Posts: 17
    • View Profile
Re: Simple way to map enemies to the techs they use?
« Reply #39 on: November 21, 2015, 05:09:33 am »
i
My programs aren't that fancy - what I have basically copies the data from a byte array to the various form elements on load, then back to a byte array on save.

Most of what I have comes from Geiger's Chrono Trigger Database. I'll try to bump up the updating of that at some point - Note to self, email Geiger about how to record some of the offsets - but it won't be until after the week of Thanksgiving.

No need to be humble, the wealth of data alone is impressive.  No matter what you're doing in there you never have to have memorized a tag or enum or bit flag value, every data type is logical instead of binary.  No head math, no cross-referencing.  And having written code working with the same structures, I know there's more logic to it than throwing the data at a form; without examining the tags, you don't even know what the output format is going to look like because the types are all different.  It's possible to imagine far less elegant and robust tools and difficult to imagine improvements, so I think this is a job well done. 

Mauron

  • Guru of Reason Emeritus
  • Errare Explorer (+1500)
  • *
  • Posts: 1774
  • Nu-chan
    • View Profile
    • Maurtopia
Re: Simple way to map enemies to the techs they use?
« Reply #40 on: November 22, 2015, 04:26:32 pm »
If you think a bunch of lines like this would help you, I can share what I have.

Code: [Select]
MPRegenCheck.Checked = ((CharacterData[0x26] & 0x20) == 0x20);
You'll probably need the notes on the Temporal Flux Plugin Architecture as well... or I could actually comment my code. I've got a few lines like:
Code: [Select]
if (G.nRomType == 2) That one's a prerelease check.

sqykly

  • Earthbound (+15)
  • *
  • Posts: 17
    • View Profile
Re: Simple way to map enemies to the techs they use?
« Reply #41 on: November 24, 2015, 12:52:55 am »
If you think a bunch of lines like this would help you, I can share what I have.

Code: [Select]
MPRegenCheck.Checked = ((CharacterData[0x26] & 0x20) == 0x20);
You'll probably need the notes on the Temporal Flux Plugin Architecture as well... or I could actually comment my code. I've got a few lines like:
Code: [Select]
if (G.nRomType == 2) That one's a prerelease check.

That's pretty much what I'm looking for.  Don't worry about comments if you don't have them; I read disassembly all the time, so even just variable names are pretty cushy.

Temporal flux is really a heck of a thing.  I'll bet it's competitive with whatever square non-coder designers used to build the game in the first place, if not nicer.   As a rule, reverse engineering projects generate dead-end, disposable code, not editing suites with a plugin architecture.  It's almost too good to just work on one game.  And needs more plain text support.  But I digress.

I'm still poking around at those wander modes, by the way.  A few things I came across:

 - $7EAEE2 is an array of wander modes
 - $7E5E0D is an array of wander modes
 - $7E974A is an array of wander modes
 - $7E9897 is an array whose elements are decremented each frame like a timer if the corresponding NPC has a non-zero wander mode that has not changed since the last frame
 - $7EA2B5 is the current wander mode for some procedures
 - $C13760 is an array of near pointers to subroutines that deal with wandering behavior on a regular basis; the one that is called for an object in wander mode W is indexed by the byte array $CCFBAB[W]
 - $CCFB91 is a byte array of small integers (0-2?) indexed by wander mode that determine the offset of some 8-byte structure in a larger structure (could be up to 0x40 bytes?) that gets copied from the rom, but there are a lot of layers of indirection.  The destinations are arrays at $7E9799 and $7E97D9, the procedure includes $C133B2, and I am gonna go cross eyed if I try to look at it again.

Is it possible that the last byte of the attack instruction in AI scripts (Unknown 3 in AId) corresponds to a wander mode in some way?  If I set Gato's to the same value as his wander mode, he attacks from the same position that he wants to wander to before using his tech.  Just a thought.

Mauron

  • Guru of Reason Emeritus
  • Errare Explorer (+1500)
  • *
  • Posts: 1774
  • Nu-chan
    • View Profile
    • Maurtopia
Re: Simple way to map enemies to the techs they use?
« Reply #42 on: November 29, 2015, 02:18:19 pm »
Which data did you want information on?

I looked into unknown 3 for attacks - It's stored in $7EAEE5, then copied to $7E5E0D,x based on enemy index - 3. Later the targeting byte is stored in $7EAEE5 for other purposes.

sqykly

  • Earthbound (+15)
  • *
  • Posts: 17
    • View Profile
Re: Simple way to map enemies to the techs they use?
« Reply #43 on: February 03, 2016, 07:31:50 am »
My target medium is a database or appendix to my (ongoing efforts toward a) mechanics FAQ, so all the offsets and bit identities used by FiendCrafter are what I would most appreciate.  HiTech consists of stuff I already know and stuff that is interesting but doesn't quite fit the DB/appendix format.  That is, I don't intend to list all of the animation instructions or the graphics pattern header.  The offsets of enemy normal attacks and PC normal attacks I do not have, or I would have to go digging for them, so if you have those handy, that would be good.  While I do find EnemyAId and its results to be really fascinating, I can't see it in a DB/appendix.  If anything, I would want to write up a grammar in PEG.js to compile a "chrono-script" to the binary the game wants, and in reverse as well.  Might go the same way for the animation instructions, but both of those are beyond the scope of my project and my free time.

My point about the relationship between attack unknown 3 and wander mode is that I can't see where else the information would come from; the enemy has to know what it's supposed to be doing before and/or after the attack animation plays until it gets its next turn.  When I changed Gato's second unknown 3 to its wander mode after its tech, it walked directly away as it normally does, then did its attack animation from afar.  In other words, the tech data for his normal attack does not specify that Gato should approach the target during the animation or where he needs to be in order to use it, so... where is that information?  Both appear to be influenced by unknown 3 due to the above.

Mauron

  • Guru of Reason Emeritus
  • Errare Explorer (+1500)
  • *
  • Posts: 1774
  • Nu-chan
    • View Profile
    • Maurtopia
Re: Simple way to map enemies to the techs they use?
« Reply #44 on: February 03, 2016, 07:32:25 pm »
Here's the FiendCrafter loading code. The notes were written for someone I shared this to ages ago.

Code: [Select]
bNoUpdate = true;
byte[] MonsterData = G.SaveRec[(byte) RecType.MonsterStats][(byte) NameBox.SelectedIndex].nData;

//Monster HP
            try
            {
                HPBox.Value = G.GetShort(MonsterData, 0x00);
            }
            catch (ArgumentOutOfRangeException)
            {
                HPBox.Maximum = 0xFFFF;
                HPBox.Value = G.GetShort(MonsterData, 0x00);
                HackRecord HPRec = (HackRecord)G.SaveRec[(byte)RecType.HPNegativeCheck][0];
                HPRec.nData = new byte[2] { 0xEA, 0xEA };
                HPRec.bModified = true;
                G.SaveRec[(byte)RecType.HPNegativeCheck][0] = HPRec;
            }
            //Monster Level
LevelBox.Value = MonsterData[0x02];

//Monster Unk0x03
            Unk0x03Box.Value = MonsterData[0x03];

//Monster Immune
            // Here we do a bitwise AND to determine if each flag is set.
            // To do this by hand, look at the binary value of two numbers.
            // If a bit is set in both numbers, it is set in the result.
            // In all of the below cases, only one bit is set.
            BlindImmunity.Checked = ((MonsterData[0x04] & 0x01) == 0x01);
            SleepImmunity.Checked = ((MonsterData[0x04] & 0x02) == 0x02);
            ConfuseImmunity.Checked = ((MonsterData[0x04] & 0x04) == 0x04);
            LockImmunity.Checked = ((MonsterData[0x04] & 0x08) == 0x08);
            HPDrainImmunity.Checked = ((MonsterData[0x04] & 0x10) == 0x10);
            SlowImmunity.Checked = ((MonsterData[0x04] & 0x20) == 0x20);
            PoisonImmunity.Checked = ((MonsterData[0x04] & 0x40) == 0x40);
            StopImmunity.Checked = ((MonsterData[0x04] & 0x80) == 0x80);

//Monster Unk0x05
            Unk0x05Box.Value = MonsterData[0x05];

//Monster Constant Statuses 1
            DoubleEvadeCheck.Checked = ((MonsterData[0x06] & 0x01) == 0x01);
            UnknownStatusCheck.Checked = ((MonsterData[0x06] & 0x02) == 0x02);
            Unused604Check.Checked = ((MonsterData[0x06] & 0x04) == 0x04);
            Unused608Check.Checked = ((MonsterData[0x06] & 0x08) == 0x08);
            Unused610Check.Checked = ((MonsterData[0x06] & 0x10) == 0x10);
            Unused620Check.Checked = ((MonsterData[0x06] & 0x20) == 0x20);
            Evade25Check.Checked = ((MonsterData[0x06] & 0x40) == 0x40);
            HasteCheck.Checked = ((MonsterData[0x06] & 0x80) == 0x80);

//Monster Constant Statuses 2
            Unused701Check.Checked = ((MonsterData[0x07] & 0x01) == 0x01);
            AttackPowerUpCheck.Checked = ((MonsterData[0x07] & 0x02) == 0x02);
            ProtectCheck.Checked = ((MonsterData[0x07] & 0x04) == 0x04);
            MaxAttackPowerUpCheck.Checked = ((MonsterData[0x07] & 0x08) == 0x08);
            Unused710Check.Checked = ((MonsterData[0x07] & 0x10) == 0x10);
            MPRegenCheck.Checked = ((MonsterData[0x07] & 0x20) == 0x20);
            BarrierCheck.Checked = ((MonsterData[0x07] & 0x40) == 0x40);
            BerserkCheck.Checked = ((MonsterData[0x07] & 0x80) == 0x80);

//Monster Unk 0x08
            Unk0x08Box.Value = MonsterData[0x08];

//Monster Speed
            SpeedBox.Value = MonsterData[0x09];
           
//Monster Magic
MagicBox.Value = MonsterData[0x0A];

//Monster Hit
            HitBox.Value = MonsterData[0x0B];

//Monster Evade
            EvadeBox.Value = MonsterData[0x0C];

//Monster Magic Def
            MagicDefenseBox.Value = MonsterData[0x0D];

//Monster Offense
            OffenseBox.Value = MonsterData[0x0E];

//Monster Defense
            DefenseBox.Value = MonsterData[0x0F];

//Monster Lightning
            LightningAbsorbCheck.Checked = ((MonsterData[0x10] & 0x80) == 0x80);
            LightningRatioBumpCheck.Checked = ((MonsterData[0x10] & 0x40) == 0x40);
            LightningDefenseComboBox.SelectedIndex = (MonsterData[0x10] & 0x3F);

//Monster Shadow
            ShadowAbsorbCheck.Checked = ((MonsterData[0x11] & 0x80) == 0x80);
            ShadowRatioBumpCheck.Checked = ((MonsterData[0x11] & 0x40) == 0x40);
            ShadowDefenseComboBox.SelectedIndex = (MonsterData[0x11] & 0x3F);

//Monster Water
            WaterAbsorbCheck.Checked = ((MonsterData[0x12] & 0x80) == 0x80);
            WaterRatioBumpCheck.Checked = ((MonsterData[0x12] & 0x40) == 0x40);
            WaterDefenseComboBox.SelectedIndex = (MonsterData[0x12] & 0x3F);

////Monster Fire
            FireAbsorbCheck.Checked = ((MonsterData[0x13] & 0x80) == 0x80);
            FireRatioBumpCheck.Checked = ((MonsterData[0x13] & 0x40) == 0x40);
            FireDefenseComboBox.SelectedIndex = (MonsterData[0x13] & 0x3F);

//Monster SettingsA
            Unk1401Check.Checked = ((MonsterData[0x14] & 0x01) == 0x01);
            AirborneCheck.Checked = ((MonsterData[0x14] & 0x02) == 0x02);
            PhysicalMissCheck.Checked = ((MonsterData[0x14] & 0x04) == 0x04);
            Unk1408Check.Checked = ((MonsterData[0x14] & 0x08) == 0x08);
            Unk1410Check.Checked = ((MonsterData[0x14] & 0x10) == 0x10);
            ImmovableCheck.Checked = ((MonsterData[0x14] & 0x20) == 0x20);
            SupportCheck.Checked = ((MonsterData[0x14] & 0x40) == 0x40);
            HPHalfDeathCheck.Checked = ((MonsterData[0x14] & 0x80) == 0x80);


//Monster SettingsB
            BossDeathCheck.Checked = ((MonsterData[0x15] & 0x01) == 0x01);
            SightScopeCheck.Checked = ((MonsterData[0x15] & 0x02) == 0x02);
            Unk1504Check.Checked = ((MonsterData[0x15] & 0x04) == 0x04);
            TypeMagicCheck.Checked = ((MonsterData[0x15] & 0x08) == 0x08);
            TypeAquaCheck.Checked = ((MonsterData[0x15] & 0x10) == 0x10);
            TypeHumanoidCheck.Checked = ((MonsterData[0x15] & 0x20) == 0x20);
            TypeMachineCheck.Checked = ((MonsterData[0x15] & 0x40) == 0x40);
            TypeUndeadCheck.Checked = ((MonsterData[0x15] & 0x80) == 0x80);

//Monster Attack 2 Index
            Attack2IndexBox.Value = MonsterData[0x16];

            byte[] RewardData = G.SaveRec[(byte)RecType.MonsterStats][(byte)NameBox.SelectedIndex].LocalRec[0].nData;
           
            ExpPointsBox.Value = G.GetShort(RewardData, 0x00);
            GilBox.Value = G.GetShort(RewardData, 0x02);
            ItemDroppedBox.SelectedIndex = RewardData[0x04];
            ItemCharmedBox.SelectedIndex = RewardData[0x05];
            TechPointsBox.Value = RewardData[0x06];

            byte[] SpriteData = G.SaveRec[(byte)RecType.MonsterStats][(byte)NameBox.SelectedIndex].LocalRec[1].nData;

            GraphicsPacketBox.Value = SpriteData[0x00];
            SpriteAssemblyBox.Value = SpriteData[0x01];
            PaletteBox.Value = SpriteData[0x02];
            AnimationBox.Value = SpriteData[0x03];
            SpriteSizeBox.Value = SpriteData[0x04];
            HandXBox.Value = SpriteData[0x05];
            HandYBox.Value =SpriteData[0x06];
            SpriteUnk0x07Box.Value = SpriteData[0x07];
            SpriteUnk0x08Box.Value = SpriteData[0x08];
            SpriteUnk0x09Box.Value = SpriteData[0x09];
            UpdatePicture();
            //EnemyDisplay.Image = DrawMonsterPicture(false);
            TargetWindowCheck.Checked = (G.SaveRec[(byte)RecType.MonsterStats][(byte)NameBox.SelectedIndex].LocalRec[2].nData[0] == 1);
            bNoUpdate = false;

These are the addresses for Enemy attacks. Attack 1 in the AI is always record 0, and Attack 2 is determined by the attack 2 index/hit effect. Order is JP, US, Pre-release.
SetRomAddr(PlugRomAddr.EnemyAttackControlData, new uint[3] { 0x0C8801, 0x0C88CA, 0x0C7B80 });
SetRomAddr(PlugRomAddr.EnemyAttackEffectData, new uint[3] { 0x0C8908, 0x0C89C6, 0x0C7C88 });

These are the pointers for player techs. I use pointers here to enable support for expansion projects. Normal attacks are at the end. Pointers are little endian, and SNES addresses. Subtract 0xC00000 for file addresses.
SetRomAddr(PlugRomAddr.PlayerTechControlPointer, new uint[3] { 0x01CBA1, 0x01CBA1, 0x01D1BA });
            SetRomAddr(PlugRomAddr.PlayerTechEffectPointer, new uint[3] { 0x01D5F0, 0x01D5F0, 0x01D252 });

This is the pointer for attack bytes, which specify which control header is used in each PC's normal attacks.
SetRomAddr(PlugRomAddr.AttackHeaderBytePointer, new uint[3] { 0x01C000, 0x01C000, 0x01B487 })