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)
[Statedef 200]
type = S
movetype = A
physics = S
ctrl = 0
velset = 0, 0
anim = 200
poweradd = 0
sprpriority = 2
[State 200, play voice]
type = playSnd
trigger1 = time = 0
value = 2, 0+random%2
channel = 0
[State 200, play se]
type = playSnd
trigger1 = animElem = 2
value = 1, 3
channel = 1
[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 = 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)))
[State Explod hit]
type = Explod
trigger1 = movehit
anim = 6000
ID = 6000
pos = (helper(222222),var(10)),(helper(222222),var(11))
postype = P1 ;P2, Front, Back, Left, Right
scale = .5,.5
sprpriority = 2
ownpal = 1
ignorehitpause = 1
persistent=0
[State Explod hit]
type = Explod
trigger1 = moveguarded
anim = 6100
ID = 6100
pos = (helper(222222),var(10)),(helper(222222),var(11))
postype = P1 ;P2, Front, Back, Left, Right
scale = .15,.15
sprpriority = 2
ownpal = 1
ignorehitpause = 1
persistent=0
[State 200, idle]
type = stateTypeSet
trigger1 = animelemtime(helper(222222),var(6))=0
movetype = I
[State 200, end of state]
type = ChangeState
trigger1 = AnimTime = 0
value = 0
ctrl = 1
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)
[State 200, play voice]
type = playSnd
trigger1 = time = 0
value = 2, 0+random%2
channel = 0
[State 200, play se]
type = playSnd
trigger1 = animElem = 2
value = 1, 3
channel = 1
Spoiler: Absolute backened that should not have to be touched aside from particular specials and supers (click to see content)
[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 = 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)))
[State Explod hit]
type = Explod
trigger1 = movehit
anim = 6000
ID = 6000
pos = (helper(222222),var(10)),(helper(222222),var(11))
postype = P1 ;P2, Front, Back, Left, Right
scale = .5,.5
sprpriority = 2
ownpal = 1
ignorehitpause = 1
persistent=0
[State Explod hit]
type = Explod
trigger1 = moveguarded
anim = 6100
ID = 6100
pos = (helper(222222),var(10)),(helper(222222),var(11))
postype = P1 ;P2, Front, Back, Left, Right
scale = .15,.15
sprpriority = 2
ownpal = 1
ignorehitpause = 1
persistent=0
[State 200, idle]
type = stateTypeSet
trigger1 = animelemtime(helper(222222),var(6))=0
movetype = I
[State 200, end of state]
type = ChangeState
trigger1 = AnimTime = 0
value = 0
ctrl = 1
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)
Animelem var(0)
Idle var(6) = var(0)+2
Damage var(1)
Guard Damage var(7)
Pause time var(2)
p2 pause time var(3) = var(2)-1
Slide time var(4) = var(2)+(var(2)/2)
Hit time var(5) = var(4)+2
RAW POWER VALUE fvar(0)
>>>>>provided as raw value
e.g. 66537
>>>>>THUS
x/4194.305 = power corrected per 1000
e.g.
4194305/4194.305 = 1000
(1 = 0 so subtract 1 from your inital result)
>>>>>(though numbers might be so big it doesn't matter)
>>>>>formula in terms of MUGEN
floor((x - 1)/4194.305)
POWER ON HIT
Severity modification fvar(2)
formula fvar(1) = floor(((fvar(0) - 1)/4194.305)*fvar(2))
POWER ON BLOCK
Severity modification fvar(4)
formula fvar(3) = floor(((fvar(0) - 1)/4194.305)*fvar(2))
>>>>>difference between a guard and a hit?
x*2 - x
>>>>>UNLESS light b/c X appears to be the root value && SNK uses
x - x
>>>>>Some musings on special values for power gain:
>>>>>light?
x*6
>>>>>heavy?
x*7
>>>>>difference between special raw && hit is equivalent to
>>>>>light?
x*2
>>>>>heavy?
x*2 + x
Spark X var(10)
Spark Y var(11)
Hitsound var(15)
extra pausetime var(20)
For each extra hit var(50)-(59)
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
; 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
; 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:
; 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.