Turned Poison into Disease through AI, but...

  • Thread starter Thread starter Armorvil
  • Start date Start date
Status
Not open for further replies.
A

Armorvil

Guest
I gave the Poison status the "Disease" effect from FFXII. In other words, when under Poison, the character's MaxHP becomes the character's CurrentHP. The consequence is, you can't restore HP to a character under Poison, anymore.

To do this, I added this script to the characters' General Counter AI scripts :

Code: [Select]
Code:
02 206000 40038070 001B12 206013 41808002 206003 41608090
It's working great, except that when Poison is finally cured, the character's maxHP doesn't return to normal. It won't continue decreasing as the character's currentHP decreases, but it won't be his original max either (until the battle is over, of course). Here is an example :

Cloud has full (2500) HP. A Bio spell is sent his way, hits him for 800 damage and poisons him. Cloud's HP is now 1700. Poison damage triggers (70), and HP is now at 1630. Tifa uses Cure2 on him. Useless : his HP stays at 1630. Cid uses an Antidote and Cloud is cured. Enemy attacks Cloud and deals 200 damage. Cloud has now 1430 HP. Tifa uses Cure2 on Cloud. Efficiency is +1500 HP, but Cloud's HP only increases to 1630 (the last maxHP the AI registered).

How can I make it, so that Tifa's final Cure2 brings him back to 2500 HP ? (there is also a disturbing little side-effect : when Poisoned, Cloud's HP increases or decreases as soon as someone or something decides to alter it - not after the attack/healing ocurred... ...but it's not so big a problem and I don't think there's a fix)
 
Last edited:
You need to store the initial MHP from the moment of infection (don't assume I counted correctly for those jumps to work).

Code: [Select]
Code:
02  206000  40038070  003302  00E0   <--or whatever60  004470  001E12  00E002  206002  4180  <-- for player characters, word values are fine.809012  206012  41808002  206002  41608090
Then when you get to the part where you heal, set the MHP back to the value you stored and set that stored value 0 so it'll work again.

This is what I'm going for if that's too hard to read:

Code: [Select]
Code:
If ( Self.Poisoned ){   If ( !(Local_Var:00E0 > 0 ) )   {      Local_Var:00E0 <- Self.MHP   }   Self.MHP <- Self.HP}
And yes, because you have this on the general counter it'll only take effect after someone performs some action on Cloud. You could put it on the Pre-Attack script and probably get the effect you want by virtue of fuzzy logic.
 
Ah, so someone's finally done this! I'd been planning to do something similar at some point, except I intended to use the Null.Health property and tie it to the poison status, eg:

counter.pre-turn {
null.health status = poison status
}

I don't know how that would interact with armours / equipment that block / guard against heal naturally though.

There's a few other things you could do with AI scripts to make poison more dangerous, too. For instance, you could use my method to make poision cause a 'zombie' status (where the character takes damage from healing), or use NFITC1's method but swap out player level rather than MaxHP (so the character becomes weaker).

Oh, and you don't need to place a script like this on every character and enemy. I would explain how, but I'm a bit pressed for time. Basically, you can write a single pre-turn script that says 'give EVERYONE the same {attribute} as their {attribute}, then use script-link to have other characters use the same script (though I haven't really tested script links...)
 
Urgh. I'm still learning, and it looks like I still have problems with AI scripting -__-

I spent a lot of time trying to pull this off, tonight. Here is your script translated in words I understand more easily (= no POP, PUSH, MASK or STACK for me :P) :

Code: [Select]
Code:
02 2060 -> Load value of Target=Self00 4003 -> Load value of Status=Poison80 -> Combine the two above (Self+Poison)70 002D -> If the above is not true, Jump to the address 0x02D. If true, continue with script:02 00E0 -> Load value of {VAR E0}60 00 -> Call for value : 0044 -> Greater than (meaning of those 3 lines : if value of {VAR E0} is greater than 0)70 001E -> If the above is not true, Jump to 0x01E. If true ({VAR E0}>0), continue with script :12 00E0 -> Load address of {VAR E0}02 2060 -> Target = self02 4180 -> MaxHP80 -> Combine the two above (Self+MaxHP)90 -> Put value (Self+MaxHP) into address of {VAR E0}12 2060 -> Load address of Target=Self12 4180 -> Load address of MaxHP80 -> Combine the two above (Self+MaxHP)02 2060 -> Load value of Target=Self02 4160 -> Load value of HP80 -> Combine the two above (Self+HP)90 -> Put value (Self+HP) into address (Self+MaxHP)
As you said this isn't complete, since when Poison is cleared, the value we saved into {VAR E0} needs to be inserted back into the address of (Self+MaxHP). This is the easy part. But what I don't get is why do we need to set the value of {VAR E0} to zero when Poison is cured (at the first jump address ?), since MaxHP will only drop again (when Cloud gets Poisoned again) if {VAR E0} is greater than zero...

I understand the logic, but here is my problem : in the beginning of your script, we need the value of {VAR E0} to be greater than 0, but we didn't set it to anything beforehand. Do all variables like this one have an initial value of 1 or something ?... ...It would have made more sense if variables had an initial value of zero (this is the case in FFVI, since, to make enemies talk once at the start of a battle, the script would look like this :

Code: [Select]
Code:
If {VAR E0} is 0,Display message : "Go guys ! Ha Ha ha... ...Give up ?"Set {VAR E0} to 1,End if
Variables like this one always being 0 at the start of a battle, it ensures the dialog box only displays once, since we set it to 1 before the condition ends. End of the FFVI parenthesis)

Sooo, because of the fact that {VAR E0} is not 0 right from the start, I fail to see how we can use it as a condition, since the other value we will load into it is also greater than 0 (Cloud's maxHP ; ie 2500).

Anyways, since my attempts at creating a working script with the above ended up with Cloud's maxHP becoming 0 (when I set {VAR E0} to 0  :-P ) and thus killing him, I tried to use Bosola's idea instead : using NullRestorative. So I made this script (with my very-personal-and-not-very-accurate AI translation :D) :

Code: [Select]
Code:
02 2060 -> self00 4003 -> poison80 -> combine70 XXXX -> if not, jump to end of script12 2060 -> load self10 42A9 -> load nullrestorative80 -> combine60 01 -> activate !90 -> Go ! :P
LOL-worthy translation, ain't it ?  ;D
And I bet you guys already see the problem ^^;
...It works, but when Cloud is healed from Poison, the NullRestorative doesn't go away... I looked through your help files in Wall Market and brainstormed until I got a headache, but I don't see how I can make use of the "if" statement to tell the game : "don't nullify Restorative if Cloud doesn't have the Poison status anymore, you retard". God, this sure isn't easy, especially for someone like me who never followed programming lessons. How frustrating.
 
Last edited:
I am pratically a n00b at AI-editing (did you remember AV? :P ) but something like this in general counter :

If Poison status = effective set var0 = 1
If Poison status = uneffective set var0 = 0
If var0 = 1 enable flag null.restorative
If var0 = 0 disable flag null.restorative
 
You have the right logic but it's not that easy to pull off, I'm afraid... And I only know how to enable something, not how to disable something.
 
I appreciate your interest, but shouldn't we wait for Bosola, NFITC1, or whoever who knows something about AI scripts to reply instead ?
 
Last edited:
Who's to say Vgr doesn't know anything about AI editing? I don't know who does and doesn't.

The Local_Var:00E0 thing is a little confusing. We're not testing if it's greater than 0 in the beginning, we're testing if it's NOT greater than 0.

If ( !( Local_Var:00E0 > 0 ) )

That exclamation mark means that result is NOT'ed. So we're logically testing if that value is less than or equal to 0. Notice that in the AI, if Local_Var:00E0 is greater than 0, then we're skipping assigning a value to it.

Also, you can easily set the NullRestorative with poison by setting it to the same value as your posion status.

12  2060
10  42A9
80
02  2060
00  4003
80
90

Now when Cloud is poisoned, Restorative elemental attacks do nothing. Then when Poison is lifted, so does that nulling. No fancy ifs are needed.
 
In fact, you can basically say, 'take a word of everyone's poison status, make that everyone's nullRestore status'. Put that on all player charactesr (or maybe one character with script links) and every monster experiences the same thing too.
 
Who's to say Vgr doesn't know anything about AI editing? I don't know who does and doesn't.
But something tells me Vgr is no expert in this field :

I am pratically a n00b at AI-editing (did you remember AV? )
:P (and yes Vrg, I do remember)

The Local_Var:00E0 thing is a little confusing. We're not testing if it's greater than 0 in the beginning, we're testing if it's NOT greater than 0.

If ( !( Local_Var:00E0 > 0 ) )

That exclamation mark means that result is NOT'ed. So we're logically testing if that value is less than or equal to 0. Notice that in the AI, if Local_Var:00E0 is greater than 0, then we're skipping assigning a value to it.
Aaahhh, now that makes sense. Thanks ! :)

Also, you can easily set the NullRestorative with poison by setting it to the same value as your posion status.

12  2060
10  42A9
80
02  2060
00  4003
80
90

Now when Cloud is poisoned, Restorative elemental attacks do nothing. Then when Poison is lifted, so does that nulling. No fancy ifs are needed.
*Brain asplodes* I'm in awe, sir. With my own vocabulary to decipher AI, this script means that you loaded the value of self+poison into the address of self+nullrestorative, huh. I find this concept hard to grasp, but I love the fact that it works ! :D Thank you very much ^^ When I understand it all, I could maybe write a "FFVII AI for the dummies" FAQ of sorts, with my own simple vocabulary (if that is even possible). ...But for now I still have to clean the gray matter that is dripping out of my right ear, and study more.

In fact, you can basically say, 'take a word of everyone's poison status, make that everyone's nullRestore status'. Put that on all player charactesr (or maybe one character with script links) and every monster experiences the same thing too.
This is really interesting. If I had known such a thing exists, I wouldn't have pasted the Seizure script into almost all enemies' AI scripts (because of this, my scene.bin grew to a whopping 304Ko).
 
Last edited:
*Brain asplodes* I'm in awe, sir. With my own vocabulary to decipher AI, this script means that you loaded the value of self+poison into the address of self+nullrestorative, huh. I find this concept hard to grasp, but I love the fact that it works ! :D Thank you very much ^^ When I understand it all, I could maybe write a "FFVII AI for the dummies" FAQ of sorts, with my own simple vocabulary (if that is even possible). ...But for now I still have to clean the gray matter that is dripping out of my right ear, and study more.
If your brain asploded from that, don't ask me how to write a function to toggle a bit at a variable position in a provided variable. Your whole face might meltdown. :)

Seems you grasped the concept pretty well, but this is less a FFVII AI trick than it is a general "assign a value to one thing when another is active" trick. It just so happens that we want the status of Poison and NullRestorative to be the same at all times. Since the game's engine is what assigns the Poison status and they're both just individual bits, just read that and indiscriminately dump that value into the NullRestore.
 
Thanks for this explanation, NFITC1 :)

@Bosola : looking at the characters' AI script, I think I see what you mean. There is an extended  (but discarded) love function in Aeris' General Counter / Death Counter / Post Attack scripts, and in Tifa's/Barret's/Yuffie's pre-battle script, there is this code :

Code: [Select]
Code:
60 037573
Which, when disassembled, translates into : "link with scripts on character : 3". Since character 3 is Aeris, I guess this means that Tifa, Barret and Yuffie share Aeris' AI scripts.

So, if I'm right and if I wanted to only enter the Null.Restore script into Cloud, all I need to do to make the other characters behave like Cloud, would be to enter this code in the other characters' Pre-Battle scripts :

Code: [Select]
Code:
60 007573
I'm gonna try this ^^ (and delete all of the useless scripts regarding love points, that will save space)

EDIT:

Mmm, Vincent has a Main script to make his Limits behave correctly, it seems - so I won't be linking him to Cloud. Or is his main script really necessary for his Limits to work ?

EDIT2:

Ah yes : the
Code: [Select]
Code:
60 0075
works perfectly ! :D Great stuff.

EDIT3:

Mmm, Vincent has a Main script to make his Limits behave correctly, it seems - so I won't be linking him to Cloud. Or is his main script really necessary for his Limits to work ?
Just answered my own question through in-game testing. Vincent really needs his main script. Without it, his limit forms do nothing when his turn comes up, and the ATB gauge just resets.
 
Last edited:
Yup, that's how you play with linked scripts. I don't know how it works if the original script holder dies or is kicked from battle, though.

Vincent's main script is absolutely vital. Without it, 'monster' Vincent just stands there doing nothing, discarding each turn which comes to him. His limit turns on the script whatever its contents.

That said, I don't know what would happen if you created a pre-turn script that set Vincent's Flag:MainScriptActive off. If it did anything, you'd probably just get a normal command menu, but a data error whenever Vincent attempted the animation for whatever you selected.

But that's a tangent. More on topic: You can also use AI scripts to make certain statuses (or spells / attacks, for that that matter) turn on counter scripts.

F'rinstance, you can write a general counter like

general counter {
if my berserk status == 0, skip this script
select last attacker and use attack
}

Other common status effects you can implement? You could create IX's trouble (albeit without damage display)

counter preturn {
if my trouble status == 0, skip this script
store my HP in a battle var
}

counter anything {
if my status == 1, do
    find difference between current HP and HP in battle var
    if difference < 1, skip
    divide difference by two
    make a group by taking active units and masking with enemies and self, creating a 'pal group'
    let pal HP = pal HP minus divided difference
make HP in battle var = 0
}

Another thing to melt your steaming stump of a neck, you can create your own status effects not tied to anything. It's simple.

counter anything {
if last attack took type and ID x and y (e.g. the id of your 'virus' spell)
    take a word from a battlevar I've assigned to record disease status
    turn my bit on
if last attack took type and ID a and b (e.g. the id of Esuna)
    take the battlevar word
    turn my bit off}

Then you can implement this status in AI scripts,

counter anything {
look up disease status word in battlevars
make my nullRestore state equal my bit in the disease status word
trollface
}
 
Just answered my own question through in-game testing. Vincent really needs his main script. Without it, his limit forms do nothing when his turn comes up, and the ATB gauge just resets.
Then put the Null.Restore on Vincent and have everyone link to him. Since their main scripts will never be activated it won't matter that their mains are linked to his.
 
Yup, that's how you play with linked scripts. I don't know how it works if the original script holder dies or is kicked from battle, though.
It still works, apparently :)

Those ideas of yours are very nice, but I'm satisfied with those status effects for now. Still, I would have been interested in implementing an Overkill function, that would have worked the same as in FFX (with the exception of doubling the item drops). IE, if an enemy is killed with a damage equal or superior to a certain number (that would vary from enemy to enemy), display the "OVERKILL" message and multiply his exp/ap/gil gain by 1.5 or 2.

It's a function I really liked in FFX, but I bet it would be tricky to accomplish (and would require a different script for every enemy, thus drastically increasing Scene.bin's size).

counter anything {
look up disease status word in battlevars
make my nullRestore state equal my bit in the disease status word
trollface
}
Trollface ?
cinoque.jpeg


Then put the Null.Restore on Vincent and have everyone link to him. Since their main scripts will never be activated it won't matter that their mains are linked to his.
D'oh ! I was afraid his main script would interfere with the other characters. Thanks, I'll do that :)
 
Last edited:
It's a function I really liked in FFX, but I bet it would be tricky to accomplish (and would require a different script for every enemy, thus drastically increasing Scene.bin's size).
No, you don't need a script per species. You can multiply and divide with AI scripts.

And if you DID need a large Scene.bin, my disk extension patch will let you use PSX SCENE.BINs 2mb in size (I believe), which is the upper limit of any usable SCENE.BIN file.
 
No, you don't need a script per species. You can multiply and divide with AI scripts.
Are you really sure ? Because if, to be overkilled, Nerosuferoth needs to be killed with 200+ damage, Zemzelett needs 400+ damage and Bottomswell needs 650+ damage, I don't see how the multiplications and divisions would help. Especially since some enemies would be overkilled when taking their HP x 150% worth of damage (for some random low-HP enemies you'd regularly 1-hit kill), while others would be overkilled when they sustained their HP x 5% or even 1% worth of damage (in the case of bosses with a lot of HP).

And if you DID need a large Scene.bin, my disk extension patch will let you use PSX SCENE.BINs 2mb in size (I believe), which is the upper limit of any usable SCENE.BIN file.
Yes, your disk extension patch is amazing. This reminds me that, as of right now, there is still only the .ppf patch for the first disk, though. So, in the worst case scenario, I could still release a different scene.bin for each disk - in which I'd delete the scenes you can't access in said disk. The resulting saved space would then be more than enough to fit into the iso, but I'd prefer to use your disk extension patch (especially for the kernel... ...I didn't check, but mine could possibly go over the PSX limit).

EDIT:

Or, about the Overkill AI scripts, one could make 3 different kinds of scripts : one for the easy random enemies, another one for the "tough" random enemies, and the last one for bosses, with a cap at 9,999.

I imagine something like this would work :

- for the easy random encounters, apply the Overkill script when they're killed by an attack dealing their HP x 80% minimum worth of damage

- for the strong random encounters, apply the Overkill script when they're killed by an attack dealing their HP x 35% minimum worth of damage, and

- for the bosses, apply the Overkill script when they're killed by an attack dealing their HP x 5% minimum worth of damage. This means that against a boss with 200,000 HP, you'd need to deal the maximum damage (9,999). And the cap would ensure that against Emerald or Ruby, the needed damage to overkill them would stay as 9,999.

You'd need to write each of these 3 scripts, then have the enemies in the same category be linked to the right script. How feasible would that be ?
 
Last edited:
Are you really sure ? Because if, to be overkilled, Nerosuferoth needs to be killed with 200+ damage, Zemzelett needs 400+ damage and Bottomswell needs 650+ damage, I don't see how the multiplications and divisions would help. Especially since some enemies would be overkilled when taking their HP x 150% worth of damage (for some random low-HP enemies you'd regularly 1-hit kill), while others would be overkilled when they sustained their HP x 5% or even 1% worth of damage (in the case of bosses with a lot of HP).
Well, it depends how you calculate overkill - I'm not actually sure how FF X handles it...

Yes, your disk extension patch is amazing. This reminds me that, as of right now, there is still only the .ppf patch for the first disk, though.
This will be resolved when I get my computer, hard drive and internet connection sorted. I'll be getting a connection tomorrow and a replacement drive around (I believe) next Wednesday.

(especially for the kernel... ...I didn't check, but mine could possibly go over the PSX limit).
One word about the kernel: whilst there's no on-disc size limit, KERNELs above 27,647 bytes *may* cause glitches. I haven't really investigated. That's still a 5kb increase, though.

Or, about the Overkill AI scripts, one could make 3 different kinds of scripts : one for the easy random enemies, another one for the "tough" random enemies, and the last one for bosses, with a cap at 9,999.

I imagine something like this would work :

- for the easy random encounters, apply the Overkill script when they're killed by an attack dealing their HP x 80% minimum worth of damage

- for the strong random encounters, apply the Overkill script when they're killed by an attack dealing their HP x 35% minimum worth of damage, and

- for the bosses, apply the Overkill script when they're killed by an attack dealing their HP x 5% minimum worth of damage. This means that against a boss with 200,000 HP, you'd need to deal the maximum damage (9,999). And the cap would ensure that against Emerald or Ruby, the needed damage to overkill them would stay as 9,999.

You'd need to write each of these 3 scripts, then have the enemies in the same category be linked to the right script. How feasible would that be ?
Feasible. There could be lots of ways of doing this, some more hacky than others. You could actually specify certain enemy IDs in the AI, but that would be bulky. You could make an enemy's prebattle script set a flag that turns on a different overkill script. You could make every 'strong' enemy turn on its bit in a targetmask held in a battlevar, then refer to that when working out which script to use. Or you could go for the simple method and make sure that only bosses have levels that are, say, multiples of twenty, and refer to this when choosing the right script.
 
Status
Not open for further replies.
Back
Top