YesNoOk
avatar

Garou: Mark of the Wolves AutoHitdef (Read 30438 times)

Started by Bannana, March 08, 2021, 02:17:25 am
Share this topic:
Garou: Mark of the Wolves AutoHitdef
#1  March 08, 2021, 02:17:25 am
  • ***
  • Non est hoc propter hoc sed propter est hoc
    • USA
    • doubletrend-zeta.neocities.org/
This will be split into three posts to maintain clarity
Part I - INTRODUCTION AND METHODOLOGY
Part II - EXPLORING THE SYSTEM.HITDEF 
Part III - PROPERLY FORMATTING THE HITDEF

Part I - INTRODUCTION AND METHODOLOGY

A majority (if not all, but I'm not going to make that argument) of games have universal pausetimes, hittimes, slidetimes, etc. for all normals and most specials (barring weird things like fireballs and supers). Garou's is incredibly simple, but is highly important for correct combo timings because all frame advantages, especially breaks, align with the pause/hit/slidetime. I dunno if anyone is looking to make more MotW characters, or even do stylistic edits in the manner of Kn (totally worth it), but this would make both of those much faster.

I personally hate the hitdef. It's inefficient because, in an efficient program, all of the valuable information concerning damage, scaling, hittime, pausetime, etc would be backend values that are called upon by the hitdef; therefore, a lot of time is wasted trudging through the cns to edit states, and I thought it would be better, and more efficient, to assign all of these functions to a file that only consists of less than 500 lines that need to be edited; moreover, because no hitdef is ever active on the first frame, so we don't have to deal with theoretical 1 tick delay, it makes perfect sense to just assign these to a helper in general.

So, in Gato+ I decided to diminish the actual amount of physical editing you have to do in any file but the AutoHitdef file,
Spoiler: leading to this (click to see content)

As far as I'm concerned, an attack state should consist of two sections:
Spoiler: Mandatory front end, e.g. sound clips on startup that are specific to each move (click to see content)
Spoiler: Absolute backened that should not have to be touched aside from particular specials and supers (click to see content)
N.B. I use explods here, so if you were using hitsparks in the hitdef then you would just put "(helper(222222),var(10)),(helper(222222),var(11))" there for the x and y pos.

So, how does this work?

essentially, in a backend file we will make a list of all values that, because they never change, should be determined ahead of time, and then define whether those values are frontend or backend, the difference being that the backend are always calculations based on frontend values.
Spoiler: After doing this, I determined my list should be as so (click to see content)

And to explain the idea of how the frontend and backend works in practice, take my top two: animelem and idle. In Garou I determined that the average time an idle would occur would generally be 2-3 animelems after the active frame, so my backend calculation for var(6) is as follows
Code:
; Idle is ALWAYS set two frames after an active
[State idle set]
type = VarSet
trigger1 = var(0)
v = 6    ;fv = 10
value = var(0)+2
persistent=1
ignorehitpause=1
This sort of backend caluclation makes hittime in Garou easy to work with because all hittimes in Garou are based upon the root p1 pausetime
Code:
; pause/hit/slide time notes
; THESE TIMES ARE ABSOLUTELY UNIVERSAL, NOTHING HAS TO BE CHANGED ASIDE FROM SPECIALS
[State p2 pause]
type = VarSet
trigger1 = var(2)
v = 3    ;fv = 10
value = var(2)-1
persistent=1
ignorehitpause=1

[State slide time]
type = VarSet
trigger1 = var(2)
v = 4    ;fv = 10
value = var(2)+(var(2)/2)
persistent=1
ignorehitpause=1

[State hittime time]
type = VarSet
trigger1 = var(4)
v = 5    ;fv = 10
value = var(4)+2
persistent=1
ignorehitpause=1
Thus, a light punch pausetime of 8 will return p2 pausetime of 7, slidetime of 12, and hittime of 14, all of which are accurate to source.

A note on severity and power gain
Prior to revealing the code we need to go over how power works in Garou.

In Garou there is a root power value, 66537 (or, when corrected, 15), that I found is compounded by degrees of severity, so that each move will be allocated a certain level of severity based on movetype, strength, whiffing, hitting, etc.
You can see in my musings above that I thought quite a bit about how the patterns point to a possible methodology for assigning power to moves, and what I decided to do was take this raw value, 66537, set it to fvar(0), and apply two sets of calculations to it:
Code:
; Calculation of raw power value
[State raw calculation]
type = VarSet
trigger1 = 1
fv = 1    ;fv = 10
value = floor(((fvar(0) - 1)/4194.305)*fvar(2))
ignorehitpause=1
persistent=1

; Calculation of guard power value
[State guard calculation]
type = VarSet
trigger1 = 1
fv = 3    ;fv = 10
value = floor(((fvar(0) - 1)/4194.305)*fvar(4))
ignorehitpause=1
persistent=1

The severity modifiers here are fvar(2) and fvar(4), which, like p1 pausetime earlier, are the front end that we will edit manually, and the resulting values will be plugged into the hitdef automatically, giving correct power gain values.
With that, let's move on to Part II, where I'll explain how the auto-hitdef file works.
Last Edit: March 08, 2021, 02:23:58 am by Bannana
Re: Garou: Mark of the Wolves AutoHitdef
#2  March 08, 2021, 02:20:09 am
  • ***
  • Non est hoc propter hoc sed propter est hoc
    • USA
    • doubletrend-zeta.neocities.org/
Part II - EXPLORING THE SYSTEM.HITDEF

Download the system.hitdef file and open it up in notepad/fighter factory/whatever. Go to line 70, which shows an example of the main type of string for normals
Code:
trigger1 = root,stateno = 200 && root,animelemtime(1)=0
trigger1 = var(0):=4 && var(1):=4 && var(7):=0 && var(2):=8 && fvar(2):=1 && fvar(4):=1 && var(10):=46 && var(11):=-69 && var(15)=0

If you've been following so far, or even already looked through the file, each of these variable assignments represents a frontend value. They're explained in the previous part, but I will explain all of them directly here

var(0) = animelem, this is the active frame of your animation (the one with the red clsn)
var(1) = damage, this is either your raw damage, to be corrected later, or the fully corrected version, whatever method you prefer
var(7) = guard damage, follows the same methodology as damage
var(2) = p1 pausetime, this is the only hittime value you need to edit, the rest is done automatically through the backend
fvar(2) = severity modifier on hit
fvar(4) = severity modifier on block
var(10) = spark pos x
var(11) = spark pos y
var(15) = hitsound

What you might have noticed is that I took all of the hitdef values that can be assigned to a variable (i.e. intergers and floats) in MUGEN and had them assigned here. This means that within about 200 lines or so you can assign all normals without having to go through your giant CNS files, saving a great deal of time!

HOWEVER, for a two hit move there is a problem! There are some issues I came up with attempting to rewrite var(0) midway through the state and recalling it as trigger2, so, since the range of 50-59 was free I decided to allocate multihits up to 10 with those.
Take note of this on line 153:
Code:
trigger1 = root,stateno = 215 && root,animelemtime(5)=0 ; *2-pt
trigger1 = var(50):=6 && var(1):=6 && var(7):=0 && var(2):=8 && fvar(2):=1 && fvar(4):=1 && var(10):=40 && var(11):=-86 && var(15)=0

Next, specials, because they give power on whiff, follow a similar pattern, but we remove the two severity modifier fvars.
Go to line 166 (and line 198 for the two hit moves):
Code:
trigger1 = root,stateno = 1000 && root,animelemtime(1)=0
trigger1 = var(0):=8 && var(1):=10 && var(7):=2 && var(2):=12 && var(10):=60 && var(11):=-63 && var(15)=12

severity for these moves is applied for each move specifically because the power gain values are different for each.
On line 207 this set of assignments shows how it works:
Code:
; These cover specials that lie outside of standard hitdef qualities
[State severity determination]
type = Null
trigger1 = root,movetype != A
trigger1 = fvar(2):=0
; throw
trigger2 = (root,stateno = 800)
trigger2 = fvar(2):=3
; light specials whiff
trigger3 = root,time<=4
trigger3 = (root,stateno = 1000) || (root,stateno = 1100) || (root,stateno = 1200)
trigger3 = fvar(2):=6
; light specials hit
trigger4 = root,time>4
trigger4 = (root,stateno = 1000) || (root,stateno = 1100) || (root,stateno = 1200)
trigger4 = fvar(2):=2
; heavy specials whiff
trigger5 = root,time<=4
trigger5 = (root,stateno = 1050) || (root,stateno = 1100) || (root,stateno = 1250)
trigger5 = fvar(2):=7
; heavy specials hit
trigger6 = root,time>4
trigger6 = (root,stateno = 1050) || (root,stateno = 1100) || (root,stateno = 1250)
trigger6 = fvar(2):=3

And for the guard values, go to line 259:
Code:
; Guard constant
[State guard determination]
type = Null
trigger1 = root,movetype != A
trigger1 = fvar(4):=0
; light specials
trigger2 = (root,stateno = 1000) || (root,stateno = 1100) || (root,stateno = 1200)
trigger2 = fvar(4):=1
; heavy specials
trigger3 = (root,stateno = 1050) || (root,stateno = 1100) || (root,stateno = 1250)
trigger3 = fvar(4):=2

Much of this should be incredibly straightforward, especially with normals, so what should be difficult is dealing with specific situations with special moves, and sometimes that means you might not be able to use the autohitdef and would be better just making a manual hitdef.
That being said, in Part III I'll explain how this all comes together into the generic hitdef.
Last Edit: March 08, 2021, 02:27:06 am by Bannana
Re: Garou: Mark of the Wolves AutoHitdef
#3  March 08, 2021, 02:23:11 am
  • ***
  • Non est hoc propter hoc sed propter est hoc
    • USA
    • doubletrend-zeta.neocities.org/
Part III - PROPERLY FORMATTING THE HITDEF

Having defined all our values, we now need to get the hitdef to recall them. This will be the easiest part and will mostly come down to copy-pasting certain sections into the hitdef.

So here's the hitdef from before, but I've bolded the areas that you can automate and italicized those you can't:

type = HitDef
trigger1 = animelemtime(helper(222222),var(0))=0
attr = S,NA        ;SCA,NA,SA,HA,NP,SP,HP,NT,ST,HT
hitflag = MAF        ;HLAFD+-
guardflag = M         ;HLA
animtype = medium          ;Light, Medium, Hard, Back, Up, DiagUp
priority = 3, Hit

damage = floor((helper(222222),var(1))*fvar(30)),floor((helper(222222),var(7))*fvar(30))
pausetime = (helper(222222),var(2)),(helper(222222),var(3))
sparkno = -1
guard.sparkno = -1
hitsound = S0, (helper(222222),var(15))+random%2
guardsound = S1, 0

ground.type = high      ;Low, Trip, None
ground.slidetime = (helper(222222),var(4))
guard.slidetime = (helper(222222),var(4))
ground.hittime = (helper(222222),var(5))
guard.hittime = (helper(222222),var(5))
guard.ctrltime = (helper(222222),var(5))
air.hittime = (helper(222222),var(5))

ground.velocity = (-2.29473946*fvar(9))-fvar(11),0
air.velocity = -2.204769373,-6.6174449*fvar(10)
airguard.velocity = -2.204769373*fvar(9),-6.6174449*fvar(10)


getpower = floor((helper(222222),fvar(1))),floor((helper(222222),fvar(1))-(helper(222222),fvar(3)))
givepower = floor((helper(222222),fvar(1))),floor((helper(222222),fvar(1))-(helper(222222),fvar(3)))


essentially, this hitdef is complete, all you have to do is follow the variables defined in the system.hitdef. Certain things such as the attributes that don't use ints or floats have to be manually changed, and as you can see I did not automate any velocities because those are highly specific to most moves.
For example, take note of how Gato's close D move works. Not only is it a two hit move, but one with drastically different attributes and velocities between hitdefs.
Code:
[State HitDef]
type = HitDef
trigger1 = animelemtime(helper(222222),var(0))>=0
attr = S,NA        ;SCA,NA,SA,HA,NP,SP,HP,NT,ST,HT
hitflag = MAF        ;HLAFD+-
guardflag = M         ;HLA
animtype = hard          ;Light, Medium, Hard, Back, Up, DiagUp
priority = 4, Hit
damage = floor((helper(222222),var(1))*fvar(30)),floor((helper(222222),var(7))*fvar(30))
pausetime = (helper(222222),var(2)),(helper(222222),var(3))
sparkno = -1
guard.sparkno = -1
hitsound = S0, (helper(222222),var(15))+random%2
guardsound = S1, 1
ground.type = low      ;Low, Trip, None
ground.slidetime = (helper(222222),var(4))
guard.slidetime = (helper(222222),var(4))
ground.hittime = (helper(222222),var(5))
guard.hittime = (helper(222222),var(5))
guard.ctrltime = (helper(222222),var(5))
air.hittime = (helper(222222),var(5))
ground.velocity = cond(animelemtime(helper(222222),var(0))=0,(-.5),(-2.29473946*fvar(9)-fvar(11))),0
air.velocity = -2.204769373,-6.6174449*fvar(10)
airguard.velocity = -2.204769373*fvar(9),-6.6174449*fvar(10)

getpower = floor((helper(222222),fvar(1))),floor((helper(222222),fvar(1))-(helper(222222),fvar(3)))
givepower = floor((helper(222222),fvar(1))),floor((helper(222222),fvar(1))-(helper(222222),fvar(3)))

[State HitDef]
type = HitDef
trigger1 = animelemtime(helper(222222),var(50))>=0
attr = S,NA        ;SCA,NA,SA,HA,NP,SP,HP,NT,ST,HT
hitflag = MAF        ;HLAFD+-
guardflag = M         ;HLA
animtype = hard          ;Light, Medium, Hard, Back, Up, DiagUp
priority = 4, Hit
damage = floor((helper(222222),var(1))*fvar(30)),floor((helper(222222),var(7))*fvar(30))
pausetime = (helper(222222),var(2)),(helper(222222),var(3))
sparkno = -1
guard.sparkno = -1
hitsound = S0, (helper(222222),var(15))+random%2
guardsound = S1, 1
ground.type = high      ;Low, Trip, None
ground.slidetime = (helper(222222),var(4))
guard.slidetime = (helper(222222),var(4))
ground.hittime = (helper(222222),var(5))
guard.hittime = (helper(222222),var(5))
guard.ctrltime = (helper(222222),var(5))
air.hittime = (helper(222222),var(5))
ground.velocity = cond(animelemtime(helper(222222),var(0))=0,(-.5),(-2.29473946*fvar(9)-fvar(11))),0
air.velocity = -2.204769373,-6.6174449*fvar(10)
airguard.velocity = -2.204769373*fvar(9),-6.6174449*fvar(10)

getpower = floor((helper(222222),fvar(1))),floor((helper(222222),fvar(1))-(helper(222222),fvar(3)))
givepower = floor((helper(222222),fvar(1))),floor((helper(222222),fvar(1))-(helper(222222),fvar(3)))

So, with a little patience, and a tiny bit of work, you'll have consistent, easily editable hitdefs according to a Garou standard. And, realistically, with a little work on the backend you could modify this for any other kind of game!

If you have any questions, don't hesitate to ask!
Last Edit: March 08, 2021, 02:29:53 am by Bannana
Re: Garou: Mark of the Wolves AutoHitdef
#4  June 05, 2021, 05:42:49 am
  • avatar
  • **
    • Vietnam
Useful for anyone who feel lazy to add more hitdef sctrl but take much more time to adjust these value and timing stuff. And you must know you also need to add at least 1 more hitdef when your character time is zero( mean the beginning of the state )when you want to create the hitdef from the beginning of the state. as reading helper value will cause your character delayed in 1 tick so your character autohitdef can't be used at the beginning of the state after the state transition(changestate, selfstate), unless you have to set these hitdef value from the statedef -1 by using the := operator and set these hitdef value instantly so that autohitdef can work at the beginning of the state.
------Tremble Mortal and Despair. Doom has come to this world------
Last Edit: June 05, 2021, 05:51:28 am by Archimonde
Re: Garou: Mark of the Wolves AutoHitdef
#5  June 09, 2021, 11:26:48 pm
  • ***
  • Non est hoc propter hoc sed propter est hoc
    • USA
    • doubletrend-zeta.neocities.org/
Correct, which is why I think this is best used for normals because they always have a proper startup time, and are usually built according to the same rules/data. Generally I would not use an autohitdef for anything specific, such as a special attack and would instead opt to make a unique hitdef.
Re: Garou: Mark of the Wolves AutoHitdef
#6  July 15, 2021, 08:54:14 am
  • avatar
  • **
    • Vietnam
Beside, you also need to change hitdef attr too, cause not every state can use the same attr like thay autohitdef and the attr is fixed, not variable like the rest.
------Tremble Mortal and Despair. Doom has come to this world------