Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - NFITC1

Pages: [1] 2 3 ... 115
Then 4160 is current HP. I’m transposing numbers in my head... I don’t remember what 41E0 is.

It looks like neither 41E0 or 2040 are set by the battle engine.

For Mandragora's part, it look like it's using 41E0 to direct AI for all the Mandragoras in the battle. If one is attacked with either physical or magical damage, then the next attacking mandragora will counter with a specific action based on the type and then declare to the others "I've avenged us".

As for 2040. The only enemies that use it are Bottomswell, Carry Armor/Left Arm/Right Arm, Elfadunk, Roulette Cannon, SOLDIER:2nd, Turks:Rude. Roulette Cannon and SOLDIER:2nd can appear in the same formation so they might be able to shed some light as to what the intended function is.

I’ll have to double check some of this, but IIRC 41E0 is the actor’s MMP. Maybe the mandragora use it as a sort of shared pool? I dunno.

As for the 22 having split damage, it certainly does. That might be the magic calf for either most magic damage actions the player has (fire, ice, etc) or magic healing (cure, et al). Either way, there is a flag on actions that can set to bypass the split damage calculations. Most likely the ones TF has listed as 22 and non-split have this override enabled.

I’ll have to check on 2040, though. It doesn’t seem familiar.

2120 is elements in the last performed action. It doesn’t matter who the actor was.

Yeah. While it is an odd choice to check for some actions and exclude one, this is still essentially intended behavior. Soldier:3rd only has 40 MP so it can only do Ice2 or Bolt2 once without the player somehow interfering (giving an ether by mistake or something). Personally, I think most human enemies should be allowed to utilize items once or twice if the need arises. Say they can use an ether (rather, an action that imitates the ether item) if their AI selects something they don't have MP for. That makes them "waste" their turn on a utility action which makes the experience of fighting "intelligent" characters more believable. This is during the ShinRa HQ raid so you'd expect company guards would have access to such supplies. But I digress...

This is definitely one of those AIs that could be simplified.

It just affects damage done by actions that inflict the status

Once you enter Midgar, you never leave. those changes don't make sense. I'm guessing you meant this:
Code: [Select]
0x040 If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 0) )
0x040 {
0x04D LocalVar:PlayerRow <- 1
0x057 If ( (BattleAddr(&AllPlayers).BattleAddr(&FrontRow) == 1) )
0x057 {
0x064 LocalVar:PlayerRow <- 2
0x06E If ( (BattleAddr(&Self).BattleAddr(&FrontRow) == 0) )
THAT would make sense. :)
I just took a look at it and at first glance the vanilla script is totally stupid. Trying to AND a value that you just set to 0 will always result in 0. However, maybe they intended this. It appears that the intended function is if all playable characters are in the back row then prioritize the machine gun action.

There is a 1:4 chance of Tonfa in the event that the LocalVar:PlayerRow = 1
There is a 1:8 chance of Tonfa if LocalVar:PlayerRow = 2
However, since neither of those are possible, it defaults to a 1:6 chance of a Tonfa

This idea probably got scrapped since both Machine Gun and Tonfa are long range attacks and their damage output is not reduced due to target's row. Rather than remove the code that would allow for this chance behavior, it is simply mitigated by not allowing it to logically happen. Then, should the need arise in the future to change it back, it's a simple fix. A SIMPLER fix is to change the initial assignment at position 0x004 to be a 3. Then these values will be correctly AND'd and produce a non-zero result. The script is still stupid for other reasons (obvious copy-pasted code blocks) but it has more function again. If I wanted to take the time I could make a much cleaner code. I'm just not sure it's worth it on such a simple enemy. Although it might provide some good practice for other scripts... I might change my mind if I find myself bored later. Actually, it is CERTAINLY not worth it because the entire script is based around the possibility that Tonfa is a short range action. Since it's not there is no need for 90% of the script. It can be as simple as
Code: [Select]
If (Not (Random MOD 6))
   LocalVar:0080 <- 272 (Machine Gun)
} Else {
   LocalVar:0080 <- 273 (Tonfa)
TargetMask <- RandomBit( AllOpponentMask )
Perform(LocalVar:0080, EnemyAttack)
That is literally all it needs to be. This prioritizes Tonfa (since it's a more damaging action) at a 5:6 ratio.

I'm not going to say I'm lazy, but can you provide a link to where I discussed that. :P I'm not doubting that I said this, but I probably said other things around that and the context would make more sense to me and be able to explain it.

Ack! I was only looking at the AI. I know those flags pull into the 402X memory region, but I always forget to look in form data.

I didn't say multiple limbs, I said multiple parts. This includes Hell House as it essentially has two "parts". One being the thing you initially encounter and the other being the possessed thing after the initial house thing is defeated.

Eligor made me rage quit. I understand what it was doing, but he got WAY more of a detailed script than he needed. It's main script is longer and convoluted than Ruby Weapon's; And HE controls his tentacles!

You have the 4020, 4021, 4023 and 4024 addresses correct to my notes. I haven't tested 4022 very much. It's used in 59 different enemies (none of which are the Hojo samples. Not sure where you get that they are) and they are typically enemies that have multiple parts and are controlling certain parts. Some exceptions are enemies like Chocobos (which is why I think this flag is "not necessary to defeat to win").

I used to check Discord a lot. I haven't been on lately due to lack of time. I'll give it a check now though. I did get that PM, but I didn't have anything constructive to add. I'm almost daily checking this site though.

Oh this is the thread for me. :D

Regarding to the (BattleAddr(&AllPlayers).BattleAddr(&Row) == 0) conundrum. This, as you noticed, translates to if there are NO players in the back row. This has to do with the data type that is returned by the AllPlayers address. It's a 2X type variable meaning it returns a list (of 3 in this case) values with the state of each player. When that is masked by row, it changes those 3 values to each players' row values. In this case, if ANY of them are zero then the condition passes. That's just the nature of the 2X variable type and conditional checks. I wish there were a non-ugly way to indicate that in the AI editor.

Now to the 4022 variable, I believe this unsetting this value indicates the enemy does not need to be defeated in order for the battle to end. I think some set pieces set this in their init scripts. Check the helicopter in the Rufus battle and the truck and tiny bronco in the Palmer battle. Just going by memory here, but I'm pretty sure they unset the 4022 flag. A few bosses do as well IIRC. Check Mystery Ninja Custom 7 script and Diamond Weapon's Death script.

FF7 Tools / Re: FFVII Enemy Database & Calculator V1.0.0
« on: 2019-02-19 18:25:18 »
In all honesty NFI, I was trying to refrain from sending you a bucket load of updates on this thing; I was trying to wait till it was complete (complete-complete) before sending it your way, but the definition of "complete" kept changing and growing along with the spreadsheet lol.

Do you even code, bruh? There is no "complete" complete. :) Apart from "final/definitive" versions, which is a developer's way of throwing up their hands and saying "I'm sick of this code and don't ever want to touch it again!", there will always be something that can be added/corrected/optimized.

FF7 Tools / Re: FFVII Enemy Database & Calculator V1.0.0
« on: 2019-02-19 12:18:34 »
This thing is absolutely amazing for what it is and I know you spent lots of time on it. It's a great tool for enemy designers to balance their creations.

I know you sent me drafts several times and I'm sorry I didn't get a chance to really try to break it. It looks fantastic! I'm not great with Excel so I'm not qualified to tell you if anything's wrong or how to fix it. Sorry. :)

FF7 Tools / Re: Gauging interest in WM Walkmesh Editor
« on: 2019-02-13 20:11:28 »
Such a thing has long been foretold by many prophets, but fate has not yet blessed us with it.

This is a very common C gotcha (attempting to dereference podhd via &(podhd->line6) is undefined behavior if podhd is NULL. The check for NULL belongs before the attempt at dereferencing podhd and really any C programmer will know that. There's nothing wrong with the "second check" except that it's misplaced, and most good compiler's (gcc, for example) will optimize it away as the attempt to dereference podhd in the previous line will let the compiler know (mistakenly) that podhd cannot be NULL.

Simple solution, check for NULL before you dereference pointers. These sorts of things are more of a hassle when you're learning than later on when you've developed good habits w.r.t NULL handling (responsible for about 90% of C's "gotchas"). Being able to propogate constraints like this and letting the compiler (or instructing it with __builtin_unreachable) optimize away unnecessary works/checks is another factor that contributes to the performance advantage well written C code enjoys over most other compiled languages.

This is not unique to C. This is poor program planning and a violation of the Fail Fast programming convention which can result in runtime errors. Most, but not all, IDEs in more modern languages will detect this and some won't even let you compile. That's with pretty strict options as it will just throw a nullpointerexception (or that language's equivalent) which might be caught outside the method.

The actual solution is always check all your arguments' states before performing any logic on them. Almost all programming errors are the result of NOT performing these checks and handling bad states correctly. The others result from creating massively long methods that try to do too much so no human can keep track of what it's actually doing and it ends up contradicting itself or another method. These errors are universal to all programming.

Just want to throw another hat in the ring. I know Pascal was supposed to be "great", but I hate it. Still hate C more, of course.

Assembly is as close to machine code as you can get. Just because it uses aliases for codes that may or may not be multibyte instead of straight binary doesn't mean the two aren't practically interchangeable. They aren't the same thing, no, but the difference is only technical.

How old are we talking? DOS-era? Some of those use some data compression voodoo in the code that can't be modified without unpacking it.

Start with something REALLY simple, like Perl. It doesn't do much, but almost every OS (Operating System. ie; Windows, Mac OS, Linux, etc) has the ability to run Perl scripts. If nothing else this will get you used to seeing syntax and how code executes when little things change. Perl is executed in real-time as a script.

From there, look to Scratch. Not the most powerful language, but it's hard to mess up. This can be compiled into a stand-alone executable.

The next logical transition would be to java (since Scratch translates 1:1 with java IIRC). You'll run into a lot of road-blocks with java when trying to access system resources. This can also be compiled and nearly every OS can run it.

So the next level would be C#. It's a less explicit form of C++ that is similar enough to java to look familiar, yet have far more functionality. This is where OSs differ. Any program can be written in C#, but it will have to be compiled with a particular OS in mind.

If you aren't scared away from that, you can dive into assembly. Start with whatever your favorite OS runs. Then you can really learn how processors work.

At least, this was my experience with a more modern linguistic twist (I learned QBASIC instead of Perl, but they can do similar things). I spent years (and still do) on each of the languages I learned and I'm still learning neat tricks they each can do.

Hi NFITC, you mention a 1.5.0 version but I don't see any links to it anywhere.  Also, are you planning on releasing the source at all?

It’s still not ready, and yes. This update is super ambitious so I want to make sure it’s right. It’ll have multilingual support too. Source will soon follow, but it’s mostly garbage TBH.

General discussion / Re: Elemental Materia
« on: 2019-01-04 13:20:51 »
No. Either just applies the "attempt to cause status to enemy" flag which has a singular use per action. Multiple hits would cause multiple attempts, but in the case of sleep you'd just as likely cause it then the next hit will snap the target out of it.

Materia do not have multiple elements. There's only one assigned to each materia. Kujata is a summon that has multiple elements attached to the action, but the materia technically has no affinity.


That's how poison works. It takes a delta of one of the actors' timers (I forget which one) and when it reaches a certain value it queues an action which inflicts poison damage to the poisoned. There are a lot of battle "events" that are mostly used by animations, but it can be used for almost anything.

That struct IS the "definitive" values for all things battle. It holds the considered HP values which get written to the save block at the end of each battle as well as which status each actor has, etc. To save on memory, redundancy is reduced as much as possible so these are the only "stable" values for each actor. When damage is calculated it will occasionally temporarily copy values to smaller parts, but the 4000+ battle addresses are what gets kept.

I see the confusion. I wasn't looking at my notes so I wasn't clear on what's going on. :P

The structure you are referring to is an array of 68h in size. The one I'm referring to is a pointer to an array that is 264h bytes in size. It does have a weird gap in its structure between index FF and 200. I'm not sure why, but it doesn't seem like those 256 byte are used.

What YOU are referring to translates to the battle variables 4000 - 42E0 for each actor (there are as many as 10, but only 9 are functionally used). Here's my notes on the structure. Some of the bytes (particularly toward the end) are unused. Some are so rarely used (possibly just once in the entirety of the game) I can't tell what they're for.

Code: [Select]
00000000 BattleVar_Actor struc ; (sizeof=0x68)   
00000000 Statuses        dd ?
00000004 ActorFlags      dd ?                    ; base 2
00000008 Index           db ?
00000009 Level           db ?                    ; base 10
0000000A Unused_0        db ?
0000000B ElementDamage   db ?                    ; base 2
0000000C CharacterID     db ?
0000000D AtkPower        db ?                    ; base 10
0000000E MagPower        db ?                    ; base 10
0000000F PhysEvade       db ?
00000010 IdleAnimID      db ?
00000011 DamagedAnimID   db ?
00000012 BackDamage      db ?                    ; base 2
00000013 SizeScale       db ?
00000014 Dexterity       db ?
00000015 Luck            db ?
00000016 IdleAnimHolder  db ?                    ; base 2
00000017 LastCovered     db ?
00000018 LastTargets     dw ?                    ; base 2
0000001A PreviousAttacker dw ?
0000001C PreviousPhysAttacker dw ?
0000001E PreviousMagAttacker dw ?
00000020 PDef            dw ?
00000022 MDef            dw ?                    ; base 10
00000024 MyIndex         dw ?
00000026 AbsorbedElements dw ?
00000028 CurrentMP       dw ?
0000002A CurrentMMP      dw ?
0000002C CurrentHP       dd ?
00000030 CurrentMHP      dd ?
00000034 anonymous_1     dd ?
00000038 anonymous_2     dd ?
0000003C anonymous_3     dd ?
00000040 anonymous_4     dd ?
00000044 InitialStatuses dd ?
00000048 anonymous_5     dd ?
0000004C anonymous_6     db ?
0000004D MagEvade        db ?
0000004E Row             db ?
0000004F Camera?         db ?
00000050 GilStolen       dw ?
00000052 ItemStolen      dw ?
00000054 Unknown         dw ?
00000056 APValue         dw ?
00000058 GilValue        dd ?
0000005C EXPValue        dd ?
00000060 anonymous_8     db ?
00000061 anonymous_9     db ?
00000062 anonymous_10    db ?
00000063 anonymous_11    db ?
00000064 anonymous_12    db ?
00000065 anonymous_13    db ?
00000066 anonymous_14    db ?
00000067 anonymous_15    db ?
00000068 BattleVar_Actor ends

the struct you speak of that's only 68h in actually 260h in size. There's a monstrous 512 alignment after that 68h that picks back up at 200h. That's where target stats exist and it can change per target. The first 68h stays consistent for the duration of the calculation.

I love the idea of an ida to json exporter. I'd happily apply it to my database. I've marked thousands of lines and don't even remember all the changes I've made. I've annotated nearly every routine used in accuracy/damage calculation and still have to hunt through them to find details.

I can answer both questions for you in excruciating detail, but I can only answer the first one right now since I don't have access to my notes right now.

That method you're seeing before and after the damage routine call is the additional effects handler. It has to run before the damage calculation and after to be able to influence pre damage and post damage calculation manipulation when applicable. The 0 and 1 are handled in the individual effects and will only apply if the action has an effect assigned. I had some detailed notes on the wiki if those still exist on one of the new ones.

Pages: [1] 2 3 ... 115