Author Topic: [FINALLY SOLVED] Trouble with AI scripting (FF7)...  (Read 8448 times)

titeguy3

  • *
  • Posts: 1283
  • A jack of all trades
    • View Profile
As you may or may not know, I've been working on remastering of the AI scripts in FF7 using PrC and an assembler that I coded myself. Normal enemies only get redone if they're too stupid/easy/pointless, or if they're glitchy, and bosses get anything from a minor tweak to a complete overhaul depending on their initial in-game difficulties (I want to make the game harder, but not IMPOSSIBLY hard--I'm trying to make it just barely doable on a lowish level playthrough).

So far everything's been smooth, until I tried to start using new concepts...

What I'm stuck on is Rude's main script for the Gelnika battle. This is what I've got (what PrC disassembly gives me):
Code: [Select]
LocalVar:0020 <- RandomBit(AllyMask)
If ( ( ( (LocalVar:0020.HP < LocalVar:0020.MHP / 2)  And Random MOD 2)  And  (Self.MP >= 64) ) )
{
TargetMask <- LocalVar:0020
Perform("Curaga"[0002], EnemyAttack)
}
ElseIf (Random MOD 2)
{
LocalVar:0000 <- RandomBit(AllActiveOpponentMask)
// EDEF(LocalVar:0000, 8 )
If ( (TargetMask.GreatestElementalDamage <= 4)   Or  (TargetMask.GreatestElementalDamage == 255) )
{
Perform("Ground Punch 2"[0102], EnemyAttack)
}
// EDEF(LocalVar:0000, 1 )
ElseIf ( ( (TargetMask.GreatestElementalDamage < 4)  Or  (TargetMask.GreatestElementalDamage == 255) )   And  (Self.MP >= 52) )
{
Perform("Firaga"[001D], EnemyAttack)
}
// EDEF(LocalVar:0000, 2 )
ElseIf ( ( (TargetMask.GreatestElementalDamage < 4)  Or  (TargetMask.GreatestElementalDamage == 255) )   And  (Self.MP >= 52) )
{
Perform("Blizzaga"[0020], EnemyAttack)
}
// EDEF(LocalVar:0000, 4 )
ElseIf ( ( (TargetMask.GreatestElementalDamage < 4)  Or  (TargetMask.GreatestElementalDamage == 255) )   And  (Self.MP >= 52) )
{
Perform("Thundaga"[0023], EnemyAttack)
}
Else
{
Perform("Shoulder Attack"[0374], EnemyAttack)
}
}
Else
{
TargetMask <- AllActiveOpponentMask
Perform("Grand Punch 2"[0165], EnemyAttack)
}
SCRIPT END

This is the assembly code, which is more informative
Code: [Select]
;Rude Main Script
PUSH11 0020
PSHA01 ALLYMASK
RBYT ; Check whether to heal either yourself or Reno
STRA
PSHA01 0020
PSHA02 HP
MASK
PSHA01 0020
PSHA02 MHP
MASK
PUSH 02
DIV
LSTN
RWRD
PUSH 2
MOD
AND
PSHA01 SELF
PSHA02 MP
MASK
PUSH #64
GEQU
AND
JMP0 SINGATK
PUSH11 TARGETMASK
PSHA01 0020
STRA
PUSH 20
PUSH 02 ; Curaga
ATTK
JUMP FIN
SINGATK RWRD
PUSH 02
MOD
JMP0 ALLATK
PUSH11 0000
PSHA01 ALLACTIVEOPPONENTMASK
RBYT
STRA
PSHA01 0000
PUSH 8 ; Earth bit
EDEF ; Load GreatestElementalDamage(Earth)
PSHA01 TARGETMASK
PSHA02 GREATESTELEMENTALDAMAGE
MASK
PUSH 4 ; 1/2 damage
LEQU ; or better
PSHA01 TARGETMASK
PSHA02 GREATESTELEMENTALDAMAGE
MASK
PUSH FF ; gotta check nothing, too
EQU
OR
JMP0 TRYFIRE
PUSH 20
PUSH 102 ; Ground Punch 2, earth damage on one opponent
ATTK
JUMP FIN
TRYFIRE PSHA01 0000
PUSH 1 ; Fire bit
EDEF ; Load GreatestElementalDamage(Fire)
PSHA01 TARGETMASK
PSHA02 GREATESTELEMENTALDAMAGE
MASK
PUSH 4 ; 1/2 damage
LSTN ; ...isn't good enough
PSHA01 TARGETMASK
PSHA02 GREATESTELEMENTALDAMAGE
MASK
PUSH FF ; gotta check nothing, too
EQU
OR
PSHA01 SELF
PSHA02 MP
MASK
PUSH #52
GEQU
AND
JMP0 TRYICE
PUSH 20
PUSH 1D ; Firaga
ATTK
JUMP FIN
TRYICE PSHA01 0000
PUSH 2 ; Ice bit
EDEF ; Load GreatestElementalDamage(Ice)
PSHA01 TARGETMASK
PSHA02 GREATESTELEMENTALDAMAGE
MASK
PUSH 4 ; 1/2 damage
LSTN ; ...isn't good enough
PSHA01 TARGETMASK
PSHA02 GREATESTELEMENTALDAMAGE
MASK
PUSH FF ; gotta check nothing, too
EQU
OR
PSHA01 SELF
PSHA02 MP
MASK
PUSH #52
GEQU
AND
JMP0 TRYBOLT
PUSH 20
PUSH 20 ; Blizzaga
ATTK
JUMP FIN
TRYBOLT PSHA01 0000
PUSH 4 ; Bolt bit
EDEF ; Load GreatestElementalDamage(Bolt)
PSHA01 TARGETMASK
PSHA02 GREATESTELEMENTALDAMAGE
MASK
PUSH 4 ; 1/2 damage
LSTN ; ...isn't good enough
PSHA01 TARGETMASK
PSHA02 GREATESTELEMENTALDAMAGE
MASK
PUSH FF ; gotta check nothing, too
EQU
OR
PSHA01 SELF
PSHA02 MP
MASK
PUSH #52
GEQU
AND
JMP0 SHOULDER
PUSH 20
PUSH 23 ; Thundaga
ATTK
JUMP FIN
SHOULDER PUSH 20
PUSH 0374 ; Shoulder Attack, non-elemental phys damage
ATTK
JUMP FIN
ALLATK PUSH11 TARGETMASK
PSHA01 ALLACTIVEOPPONENTMASK
STRA
PUSH 20
PUSH 0165 ; Grand Punch 2, hits everyone
ATTK
FIN END

What this SHOULD do is determine if earth type magic is effective against a chosen opponent, then if it isn't, move on to other elements and finally, default to a physical attack if all elements are a no-go. Instead, Rude constantly defaults to the PHYSICAL attack regardless of elemental immunity (meaning it thinks every one is resistant to every element, which they're not).

Any input/inquiries would be gladly appreciated. Thanks.

Edit: I've switched my code over to using GreatestElementalDamage in conjunction with EDEF, and I got my values straight from the wiki, something's not right here...
« Last Edit: 2009-07-31 00:09:28 by titeguy3 »

Kudistos Megistos

  • Banned
  • *
  • Posts: 3929
    • View Profile
Re: Trouble with AI scripting (FF7)...
« Reply #1 on: 2009-07-30 00:09:51 »
This might shed some light on it, although it seems that no-one could find a completely satisfactory solution.

titeguy3

  • *
  • Posts: 1283
  • A jack of all trades
    • View Profile
Re: Trouble with AI scripting (FF7)...
« Reply #2 on: 2009-07-30 00:50:52 »
This might shed some light on it, although it seems that no-one could find a completely satisfactory solution.
That's not an issue with my rude anymore. I deleted his scripts and started them anew.

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: Trouble with AI scripting (FF7)...
« Reply #3 on: 2009-07-30 00:59:47 »
I don't know about your problem, titeguy3, but the original Rude here can be "fixed" by changing byte 0x005 from 40 to 90. This will reset his "I didn't hit Tifa" flag and his script will continue like normal. I think the intention was if he DOES hit Tifa that he wouldn't attack anyone again, but this fails pretty hard. ;) His whole script is just a big wad of mess.

titeguy3

  • *
  • Posts: 1283
  • A jack of all trades
    • View Profile
Re: Trouble with AI scripting (FF7)...
« Reply #4 on: 2009-07-30 01:10:55 »
I've completely removed the whole "Rude cares about tifa" aspect of Rude's AI for now...

The problem I'm having is with AbsorbElements(x4130) and NullElements(x42A0). I'm trying to make Rude know before attacking whether or not an attack is gonna be absorbed or nullified, but it's not working, I need some confirmation on how to use these variables.

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: Trouble with AI scripting (FF7)...
« Reply #5 on: 2009-07-30 03:00:52 »
Try using the elusive 96h command. The one that Sephiroth has. That might set resistance variables. Not much of it is known. I think Akari told me what he found, but I don't remember exactly what it was. It DOES set the 4058 of an enemy.

titeguy3

  • *
  • Posts: 1283
  • A jack of all trades
    • View Profile
Re: Trouble with AI scripting (FF7)...
« Reply #6 on: 2009-07-30 03:56:30 »
Thanks for the reply!
It looks like Sephi uses Edef(AllActiveOpponents, 8 ), I'll try that out, then try different second arguments if that doesn't work. Do you know what the significance of the 8 is by any chance?

EDIT: I've tried it with x0-xA, x10, and x20, and still no results...

I notice that Sephiroth's script uses the notion that

If(TargetMask.GreatestElementalDamage >= 5), TargetMask is resistant to Earth

but I don't quite get the functionality of GreatestElementalDamage (x4058), or ElementalWeakness (x4050) for that matter. I'm thinking perhaps it has something to do with the element order(?):
Code: [Select]
0       0x0000 Non Elemental
1       0x0001 Fire
2       0x0002 Ice
3       0x0004 Lightning
4       0x0008 Earth
5       0x0010 Poison
6       0x0020 Gravity
7       0x0040 Water
8       0x0080 Wind
9       0x0100 Holy
10      0x0200 Restorative
11      0x0400 Cut
12      0x0800 Hit
13      0x1000 Punch
14      0x2000 Shoot
15      0x4000 Shout
16      0x8000 Unknown
So if GreatestElementalWeakness is >= 5, its > 4, which would be Earth....as well as Fire, Ice, and Lightning...So maybe what this is supposed to do is something along the lines of:
"If [Fire/Ice/Lightning/Earth] Magic is effective, use magic. Else attack physically."
So maybe I can try something similar with Rude's AI? I'll make some changes and post the results...
« Last Edit: 2009-07-30 05:49:36 by titeguy3 »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: Trouble with AI scripting (FF7)...
« Reply #7 on: 2009-07-30 05:38:08 »
The 8 happens to be the element in question. You can use this to find the greatest elemental weakness of each enemy.

titeguy3

  • *
  • Posts: 1283
  • A jack of all trades
    • View Profile
Re: Trouble with AI scripting (FF7)...
« Reply #8 on: 2009-07-30 05:55:16 »
I see... but if the 8 is supposed to represent the Earth element bit... what's the point of this statement:
Code: [Select]
TargetMask <-  (TargetMask.GreatestElementalDamage >= 5)
If (TargetMask)
{
Debug.Print: "RESIST EARTH MONSTER" ; 0
LocalVar:0040 <- 1
}
Does the EDEF opcode tell the game to load elemental data into memory? Because as far as I can tell, it doesn't put anything onto the stack, and if that is the case, I don't know why it would be necessary to specify an element.

EDIT: The theory in my previous post was a bust. I'm clueless regarding how to use these variables... I have confirmed, however, that for party members, AbsorbElements and NullElements are always zero even after calling Edef
DOUBLE-EDIT: I think I've got it! GreatestElementalDamage in conjunction with EDEF gives the Elemental Effect of that element on that character in this order:
Code: [Select]
x00 = no effect (x1 damage)
x01 = "never miss"
x02 = "Death" damage (like flash)
x03 = x2 damage
x04 = 1/2 damage
x05 = nullify damage (0)
x06 = absorb damage
x07 = "Recovery" damage (like Great Gospel)
so in sephy's example, by checking if TargetMask.GreatestElementalDamage (the name should be changed if this is right) >= 5 after EDEF(AllActiveOpponentMask, 8 ), you're checking if earth (x08) is nullified or worse by an enemy in [AllActiveOpponentMask].
« Last Edit: 2009-07-30 18:31:19 by titeguy3 »

nfitc1

  • *
  • Posts: 3011
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: Trouble with AI scripting (FF7)...
« Reply #9 on: 2009-07-30 19:34:47 »
DOUBLE-EDIT: I think I've got it! GreatestElementalDamage in conjunction with EDEF gives the Elemental Effect of that element on that character in this order:
Code: [Select]
x00 = no effect (x1 damage)
x01 = "never miss"
x02 = "Death" damage (like flash)
x03 = x2 damage
x04 = 1/2 damage
x05 = nullify damage (0)
x06 = absorb damage
x07 = "Recovery" damage (like Great Gospel)
so in sephy's example, by checking if TargetMask.GreatestElementalDamage (the name should be changed if this is right) >= 5 after EDEF(AllActiveOpponentMask, 8 ), you're checking if earth (x08) is nullified or worse by an enemy in [AllActiveOpponentMask].

Yes, but Flash just attempts to deal the death status effect and Great Gospel heals 100%. They don't use these elemental modifiers this way.
You can do the EDEF(AllActiveOpponentMask, 8); TargetMask.GreatestElementaDamage < 4; to see if they're vulnerable to earth. I believe that it also populates the TargetMask as well. But it does retrieve all targets' elemental modifiers for that element and stores it in that target's GreatestElementalDamage.

Xelane

  • *
  • Posts: 477
    • View Profile
.....i want to understand wtf you guys are talking about so badly....

i wish i could understand programming like that but it looks so difficult...

i didnt even bother reading past the 3rd page of this c script manual becuase i was so baffled at the complexity  :oops: and :cry:

titeguy3

  • *
  • Posts: 1283
  • A jack of all trades
    • View Profile
.....i want to understand wtf you guys are talking about so badly....

i wish i could understand programming like that but it looks so difficult...

i didnt even bother reading past the 3rd page of this c script manual becuase i was so baffled at the complexity  :oops: and :cry:

Haha. If it's any consolation, I wish I knew how the hell to create, texture or import models.

Or how to use meteor for that matter  :-D

Okay I just read the wiki, and I had the hex values wrong... The way it actually is is:
Code: [Select]
x00 - Death
x01 - Never miss (?, this wasn't in the wiki but it's in Hojo, go figure)
x02 - Double Damage
x03 - Normal, apparently
x04 - Half Damage
x05 - Nullify Damage
x06 - Absorb 100%
x07 - Full Cure
xFF - Nothing
So the reason mine isn't working is because FF > 4, apparently, so I have to put in a check for that... [sigh] :roll: Time to input the code for the 100th time and see if it works...
EDIT: just added a check for FF and rude just attacks physically still...
« Last Edit: 2009-07-30 21:09:33 by titeguy3 »

Akari

  • *
  • Posts: 766
    • View Profile
0 - death, 2 - 200%damage, 3 - normal, 4 - 50%damage, 5 - 0%damage, 6 - 100%absorb, 7 - fully recover

titeguy3

  • *
  • Posts: 1283
  • A jack of all trades
    • View Profile
It works if i use these values....
Code: [Select]
none = nothing, there's literally no value for when there isn't a modifier...check for this case by checking for NOT(the other three)
half = x0400
null = x0500
absorb = x0600

since those are the only elemental attributes available to the player. Don't ask me why though. That's just the way it is. [updates Rude's AI]

IMPORTANT EDIT
: This ended up being a stupid mistake on my part. I was calling GreatestElementalDamage as a word, whereas it's actually a byte, and that was causing my numbers to be left shifted twice. Problem solved, and I'll be more careful about my typecasting.
« Last Edit: 2009-08-03 23:53:59 by titeguy3 »