YesNoOk
avatar

Fix all your command issues with the EXPLODsive Buffering system! (Read 4563 times)

Started by Jmorphman, August 03, 2017, 09:52:01 pm
Share this topic:
Fix all your command issues with the EXPLODsive Buffering system!
#1  August 03, 2017, 09:52:01 pm
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
TABLE OF CONTENTS

QUICK LINKS
Spoiler, click to toggle visibilty


INTRODUCTION AND SYSTEM OVERVIEW

What is this? What exactly does a buffering system do?
This is an explod-based buffering system, designed to overcome several different errors and bugs related to MUGEN's default method of processing commands, as well as offering creators more control over how the commands of their character should work. It is designed to be easy to use, understand, and install. It was developed by JustNoPoint and Jmorphman.

Is this really needed? What exactly is wrong with MUGEN's command system anyway?
MUGEN's default command system has many limitations: commands typically get broken whenever the characters switch sides, requiring players to start over any commands they've started to input if their character happens to switch sides with the opponent; there are also issues involving directional release commands, charge commands, and more. MUGEN also doesn't offer a ton of flexibility in defining commands: you can set a time limit for how long a player has to perform an overall command, but you can't define individual time limits for specific inputs/elements in those commands. With this new buffering system, all those problems are no more, all with zero input lag!

How does it work?
The system generates a series of explods, all with unique ID's, each representing the different inputs that make up different commands; these explods are created depending on the current input of an invisible helper that is created at the start of the match. Instead of checking MUGEN's own command system to see whether a command has successfully been performed, we check to see if a particular explod exists. For instance, if an explod that represents the "forward" input of a "quarter-circle forward" command exists, then we know that the player has successfully completed a "quarter-circle forward" command.

Why do we need a helper?
We use a helper so that we can process all commands through it; the helper we create has keyctrl = 1, which we use to allow the character to read any inputs the player has entered; the helper also always face the same direction, so that its own commands never, ever change direction, allowing the character to always know when a player has entered "left" or "right", no matter what direction the character is facing. Using a helper to process also eliminates a problem involving commands during hitpause: if a character is in HitPause (but only from attacking an opponent, this does not occur when a character is hit by their opponent and placed into that opponent's HitPause) MUGEN will essentially set the default "command.buffer.time" to the HitPauseTime, meaning that each and every input a player enters in during the HitPause is kept active until the HitPause ends. This makes it impossible for the system to distinguish between, say, a QCF command and a QCFx2 command: because each of the "down", "down-forward", and "forward" inputs are active on every tick during the hitpause, the system would read a single QCF motion as a QCFx2 motion. By checking only the Helper's own input, we can avoid this problem entirely, as the Helper will not be affected by the HitPause at all!

If we're using a helper why not store these commands in the helper's variables, instead of using explods?
Unfortunately, doing this will result in a one-frame input delay. But checking only the helper's commands results in no delay, so we have the character read the helper's inputs and store them as explods; in this fashion, we can avoid using up the player's own variables, while still introducing no input lag into the character.

How do I use it? How difficult is it to add to a character?
The system is (hopefully) relatively easy to understand, and is pretty simple to add. The system is also completely customizable, and can be modified to suit the preference of the creator. However, it can also be installed as is; requiring only minor modifications to your character's code.

If you don't want to customize anything, or add any new commands beyond the default ones JustNoPoint and I have already defined, you can copy and paste the various blocks of code needed to install the system. Then, you simply need to change the triggers for the various ChangeState controllers in State -1. For detailed instructions on how to add the system, please see the following post.



The basic concept of this system is that we generate a series of explods based on the input of the player; for example, if the player hits "down", an explod—with a unique ID, representing the "down" direction—will be created, with a certain RemoveTime that will serve as the buffer window. If the player then hits "down-forward" within that buffer window (in other words, before the "down" explod has been removed), a "down-forward" explod will be created. This process will repeat if the player then inputs "forward". If MUGEN checks to see if that "forward" explod exists, then it will know that a quarter circle forward motion has been successfully completed. This is what we use as triggers: instead of checking to see if command = "QCF", we check NumExplod(QCF). We use explods instead of variables in this system to keep the character's own variables (mostly) free.

However, unlike MUGEN, we won't be using "forward" and "back"; the explod system is based entirely on absolute, not relative, directional inputs. That means left and right, instead of forward or back. This is to avoid problems in situations where the characters are switching sides, and their commands reverse. By defining everything in terms of left and right, we can ensure that a player can always perform the move they want to perform, and not have engine limitations get in the way. To get a better idea of why this is needed, take a look at this gif:



Even though Ryu's player starts off by inputting "down", and then "down-forward" (you can also think of this as a "down-right" input), soon after entering "down-forward", Ryu and Chun-Li switch sides. Then, Ryu's player inputs "back" (or rather, a "right" input) and hits light kick, and Ryu still performs Tatsumaki Senpukyaku. If we were using a system that used relative (forward/back) directions only, Ryu's input would look like "down, down-forward, back + LK", and he certainly wouldn't perform his Tatsu given that input!

This is why we use a system with absolute (left/right) directions; since everything is defined in terms of left and right, we always know what the player's input has been. In this case, Ryu's input would defined as a "down, down-right, right + LK" motion. And since Ryu is facing to the left when that command is finished, that means Ryu should be allowed to perform a Tatsu, so he does!

This also makes it possible to correctly buffer commands while jumping over an opponent. Let's pretend that in the previous example, Ryu was jumping over Chun-Li, and wanted to perform a Hadoken when he landed. He'd be facing to the right for his entire jump, even after passing over Chun-Li; if the player starts inputting "down, down-left" while still in the air, and then performed "left + LP" when Ryu landed, Ryu will fire off a Hadoken. If we were using a system based on forward/back, that command would turn into "down, down-back, forward + LP", since Ryu is facing to the right when the first two directions are input! This is why defining everything in terms of left/right is so important.

Using a system based on left/right does mean, however, that for each individual command we want to add to a character, we have to define two separate versions: one for when the character is facing the left, and one for when they're facing the right. For a quarter-circle forward motion, we would need a "down, down-right, right" command, and a "down, down-left, left" command, even if the character we're working on only has a quarter-circle forward command, and not a quarter-circle back command!

Here is a very simplified version of a Hadoken ChangeState, using MUGEN's in-built command parsing system:

Code:
[State -1, Hadoken (light strength)]
type = ChangeState
value = 1000
trigger1 = command = "QCF+x"

To adapt this to the EXPLODsive Buffering system, we first look up the explods (using the handy chart at the bottom of the next post, or at the top of the EXPLODsive Buffering system code) that represent quarter circle right/left, the explod representing a press of the "x" button, and adding the appropriate triggers. In this case, we find that a quarter-circle right motion corresponds to 90010006, a quarter-circle left is 90010104, and x is 90000200. So, we modify the ChangeState to look like this:

Code:
[State -1, Hadoken]
type = ChangeState
value = 1000
trigger1 = (ifElse(P2dist x < 0, Facing = -1, Facing = 1) && NumExplod(90010006)) || (ifElse(P2dist x < 0, Facing = 1, Facing = -1) && NumExplod(90010104))
trigger1 = NumExplod(90000200)

It might look a little more imposing than it used to be, but it's still pretty simple! The first trigger will activate if the player is facing to the right and an explod exists that represents a "quarter-circle right", or if the player is facing to the left and an explod exists that represents a "quarter-circle left". The second line will activate when an explod representing "x" exists; if both triggers are true, then that must mean that the player has successfully input a "QCF+x" command, which means it's time to fire off a Hadoken!

There is one additional wrinkle: we also have to check whether or not P2dist x is less than zero. This is for situations where the character has jumped over their opponent, but they haven't turned around yet. If P2dist x is less than zero, then that means the character has switched sides with the opponent, but has not yet actually turned around yet. If that's the case, then we essentially treat the character's facing as being reversed, so we look at the opposite direction when trying to determine if the character is able to perform this move or not. For moves performed in the air, this situation will occur as soon as the character has passed over their opponent, but for ground moves, this situation will only occur because of a MUGEN bug, where a character that lands from a jump that passes over their opponent will take one tick longer than they should to change the direction they're facing. Fortunately, we can determine if this is happening by checking if the P2dist x is less than zero, just like we would with moves done in the air!
Last Edit: August 11, 2017, 04:09:49 am by Jmorphman
Re: Fix all your command issues with the EXPLODsive Buffering system!
#2  August 03, 2017, 09:52:12 pm
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
INSTALLATION INSTRUCTIONS

In this section, we will explain how to add the default, unmodified system to a character.

The posts following this one will go into greater detail about the system, including how to remove unneeded commands (so that you aren't wastefully creating a bunch of explods that will never be used), how to handle aerial moves and charge moves. If you want to add more moves to the system (beyond what the default version provides), or are interested in customizing it, please see the final post, an advanced tutorial which also contains few other details that most people interested in very faithfully adapting a particular game's input system to MUGEN will find helpful.

Adding this system will primarily involve editing your character's .cmd file, but you must also add a few things to their -2 State. It is very important that things be placed in the correct place, or else they won't work!

You might find it easier to start fresh with a new .cmd that already contains the buffering system, although you'll still need to add two things to the character's other code file(s). If you wanted to use this method, simply download JustNoPoint's updated Kung Fu Man and use its .cmd file, which already has the buffering system applied.

If you want to use your own .cmd, it'll still be pretty simple! The first step to "install" this system is to open up your .cmd and set the "command.buffer.time" to 1. This will ensure the buffer system can correctly read what the player is inputting at any specific moment in time. If you were using a "command.buffer.time" larger than 1 to make commands more lenient, have no fear! The buffering system can be modified to produce the exact same effect.

In addition, you need to add the following commands to your .cmd file:

Code:
[Command]
name = "dfwd"
command = DF
time = 1

[Command]
name = "dback"
command = DB
time = 1

[Command]
name = "ufwd"
command = UF
time = 1

[Command]
name = "uback"
command = UB
time = 1

These make it easier to enter new commands, and they're all used by the buffering system.

Finally, follow this link, and copy and paste the entire contents into the start of your -1 State, which is usually located in the character's .cmd file. It's vital that this block of code is placed at the very top of State -1!!!



No matter which method of updating your .cmd you choose, you will also have to add some stuff to your character's normal code files. You must create a helper, which this system will use to process the player's input. Just copy and paste the following into your character's State -2:

Code:
[State -2, EXPLODsive Buffering Helper]
type = Helper
trigger1 = !NumHelper(90000005)
helpertype = Normal
stateNo = 90000005
ID = 90000005
name = "EXPLODsive Buffering helper"
posType = P1
facing = ifElse(Facing = 1, 1, -1)
keyCtrl = 1
ignoreHitPause = 1

The helper state must also be added. Make sure not to place it inside another state!

Code:
;========================<EXPLODSIVE BUFFERING HELPER>=============================
[StateDef 90000005]
type = A
physics = N
moveType = I
anim = 1
velSet = 0,0
ctrl = 0

[State 90000005, DisplayToClipboard]
type = DisplayToClipboard
trigger1 = 1
text = "by JustMorphPointman"
ignoreHitPause = 1

"anim = 1" is just an example here; it can be any animation number you want, as long as it's a blank one!

You must also make sure that int variables 47, 48, 49, and 50 are free, if you are including the charge moves. These variables are used by the system to store charge move buffer times and other information needed to have charge move behave properly. If you are not using the charge moves at all, you can safely make use of those four variables, as long as you delete the lines of code in the buffer system that deal with charge moves (for more information about deleting unneeded moves, see this post.

Now you just need to switch out the triggers in your character's ChangeStates in -1, and you'll be good to go!



Most moves (special, super, or otherwise) can be pretty easily updated to use this system, just by copying and pasting the appropriate triggers. For moves that require one or more directional input, this statement (with the appropriate explod ID replacing the text in all caps) should be used (except in rarer cases covered below):

Code:
triggerAll = (ifElse(P2dist x < 0, Facing = -1, Facing = 1) && NumExplod(DIRECTIONAL COMMAND WHEN FACING RIGHT)) || (ifElse(P2dist x < 0, Facing = 1, Facing = -1) && NumExplod(DIRECTIONAL COMMAND WHEN FACING LEFT))

The above statement is how the vast majority of fighting game moves on both the ground and the air work. However, there are a few games that use different rules for air commands, such as the Marvel series and Vampire Savior/Darkstalkers 3. For in-depth discussion about how those games work, and what the triggers you should use if you want to replicate that behavior please see this post.



Buttons can be handled a lot more easily, fortunately. Just check to see whether the appropriate button has been input (or released) using NumExplod. You can check for both a button press, and a button release command, and you can also check for multiple buttons by chaining together NumExplod statements with MUGEN's logical operators. For example, this is an easy, concise way of checking to see if two punch buttons have been input:

Code:
triggerAll = (ifElse(NumExplod(90000200), 1, 0) + ifElse(NumExplod(90000210), 1, 0) + ifElse(NumExplod(90000220), 1, 0) >= 2)

Do note the ifElse statements: if we were just checking "NumExplod(x) + NumExplod(y) + NumExplod(z) >= 2", we could get that to trigger just be tapping x rapidly enough, since buttons have a default buffer time of 3 (as in, each time you input a button, that input will stay active for 3 ticks). Instead, we just check to see whether the number of each button explod is non-zero, and if it is, then the ifElse statement will return 1.



You might also want to increase the "ExplodMax" in mugen.cfg. The default value is 256, which should be more than enough for two human players using characters with this system (with room to spare; even the default Buffering system, without removing any unneeded commands, will only create around 40 explods at a time, and only will make that many if the player is mashing randomly). But if you have an extremely explod-heavy character (and we mean making hundreds of explods), it's theoretically possible to bump into the 256 explod limit, so you might want to increase it to something higher.



The following is a list of the ID numbers that correspond to all the commands that currently exist in the default version of the EXPLODsive Buffering system:
Code:
BASIC DIRECTIONAL INPUTS

Down: 90000002
Down-right: 90000003
Right: 90000006
Up-right: 90000009
Up: 90000008
Up-left: 90000007
Left: 90000004
Down-left: 90000001


BUTTON PRESS
x: 90000200
y: 90000210
z: 90000220
a: 90000230
b: 90000240
c: 90000250
start: 90000195


BUTTON RELEASE
x: 90000205
y: 90000215
z: 90000225
a: 90000235
b: 90000245
c: 90000255
start: 90000196

SYSTEM MOTIONS
;Down (3 ticks): (D with a 3 tick buffer) 90000012
;Down-right (3 ticks): (DR with a 3 tick buffer) 90000013
;Right (3 ticks): (R with a 3 tick buffer) 90000016
;Up-right (3 ticks): (UR with a 3 tick buffer) 90000019
;Up (3 ticks): (U with a 3 tick buffer) 90000018
;Up-left (3 ticks): (UL with a 3 tick buffer) 90000017
;Left (3 ticks): (L with a 3 tick buffer) 90000014
;Down-left (3 ticks): (DL with a 3 tick buffer) 90000011
R, R: (F, F) 90001016
L, L: (B, B) 90001064
D, U: (D, U) 90000408
U, U: (U, U) 90000518
D, D: (D, D) 90000562
R, DR, D (F, DF, D) 90007502
L, DL, D (B, DB, D) 90007552


SPECIAL MOTIONS
D, DR, R: (QCF) 90010006
D, DL, L: (QCB) 90010104
R, D, DR: (F, D, DF) 90010203
L, D, DL: (B, D, DB) 90010301
L, DL, D, DR, R: (HCF) 90010406
R, DR, D, DL, L: (HCB) 90010504
(charge) L, R: ([charge] B, F) 90010606
(charge) R, L: ([charge] F, B) 90010704
(charge) D, U: ([charge] D, U) 90010808
L, D, R, U: (360) 90011008
L, U, R, D: (360) 90011102
D, R, U, L: (360) 90011204
D, L, U, R: (360) 90011306
R, D, L, U: (360) 90011408
R, U, L, D: (360) 90011502
U, R, D, L: (360) 90011604
U, L, D, R: (360) 90011706
R, D, L, R: (HCB, F) 90012016
L, D, R, L: (HCF, B) 90012114
R, L, R: (F, B, F) 90012216
L, R, L: (B, F, B) 90012314
D, DR, R, UR: (QCF, UF) 90012409
D, DL, L, UL: (QCB, UB) 90012507
D, D: ([special] D, D) 90012602


SUPER MOTIONS
D, DR, R, D, DR, R: (QCFx2) 90030016
D, DL, L, D, DL, L: (QCBx2) 90030114
L, DL, D, DR, R, L, DL, D, DR, R: (HCFx2) 90030416
R, DR, D, DL, L, R, DR, D, DL, L: (HCBx2) 90030514
(charge) L, R, L, R: ([charge] B, F, B, F) 90030616
(charge) R, L, R, L: ([charge] F, B, F, B) 90030714
(charge) DL, DR, DL, UR: ([charge] DB, DF, DB, UF) 90030809
(charge) DR, DL, DR, UL: ([charge] DF, DB, DF, UB) 90030907
L, D, R, U, L, D, R, U: (720) 90031018
L, U, R, D, L, U, R, D: (720) 90031112
D, R, U, L, D, R, U, L: (720) 90031214
D, L, U, R, D, L, U, R: (720) 90031316
R, D, L, U, R, D, L, U: (720) 90031418
R, U, L, D, R, U, L, D: (720) 90031512
U, R, D, L, U, R, D, L: (720) 90031614
U, L, D, R, U, L, D, R: (720) 90031716
R, L, D, R: (F, HCF) 90032016
L, R, D, L: (B, HCB) 90032114
D, DR, R, D, L: (QCF, HCB) 90032204
D, DL, L, D, R: (QCB, HCF) 90032306
D, DR, R, DR, L: (QCF, DF, B) 90032404
D, DL, L, DL, R: (QCB, DB, F) 90032506
D, D, D: (D, D, D) 90032602


SUPER MOTIONS (button inputs)
x, x, F, a, z: (Shun Goku Satsu) 94000220
Last Edit: August 15, 2017, 02:43:31 am by Jmorphman
Re: Fix all your command issues with the EXPLODsive Buffering system!
#3  August 03, 2017, 09:52:38 pm
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
REMOVING UNNEEDED MOVES

The default, unmodified EXPLODsive Buffering system includes many, many commands, even if we tried to limit ourselves to only the most commonly seen ones in fighting games (and a few bonuses). But no character's gonna use all of them at once! And why have a character generate a bunch of explods (albeit, invisible ones) that don't get used for anything? That's just a wasteful use of resources.

Instead, we strongly recommend that after you finish adding the EXPLODsive Buffering system to your character, you go back and remove all commands your character isn't using. This is a relatively simple process: since everything is clearly marked, it's pretty easy to just scroll through the code that makes up the EXPLODsive Buffering system, and remove all the commands you won't be using.

There is one thing you should watch out for, however. Some commands are "linked" to other commands. These commands use portions of a command that appeared earlier in the system in order to avoid needlessly making duplicate explods. One example of this would be the double quarter circle motion: instead of having a bunch of explods representing a single quarter-circle motion for a QCF/QCB command, and then another set of explods representing the first and second quarter-circle motions for a QCFx2/QCBx2 command, the system is set up so that the QCFx2/QCBx2 command only uses a single set of explods, representing the second quarter-circle motion that makes up the command. We can rely on the explods representing the QCF/QCB command to tell us when a player has made the first quarter-circle motion, and keep things much tidier.

In other words, these commands are derived from commands that are placed above them in the system. If you are using a command of this type, then you absolutely can NOT remove the command that it is linked to, or else the linked command will cease to function! If you're using any of the commands listed below, you must ensure that you don't remove the command they're derived from!
Code:
BASE COMMAND			DERIVED COMMAND

QCF / QCB -> {QCF,UF / QCB,UB}; {QCFx2 / QCBx2}; {QCF, HCB / QCB, HCF}; {QCF, DF, B / QCB, DB, F}
HCF / HCB -> HCFx2 / HCBx2
(charge) B, F -> (charge) B, F, B, F / (charge) F, B, F, B
360 -> 720
D, D -> {[special] D, D}; {D, D, D}
Last Edit: August 03, 2017, 09:58:11 pm by Jmorphman
Re: Fix all your command issues with the EXPLODsive Buffering system!
New #4  August 03, 2017, 09:52:50 pm
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
HANDLING AIR COMMANDS (AND CAPCOM TURNING)

HOW TO HANDLE AIR COMMANDS

As mentioned above, the vast majority of all fighting games (ranging from games such as Street Fighter Alpha 2, to King of Fighters 2000, to Night Warriors/Darkstalkers 2, to Capcom vs SNK 2) reverse the player's commands whenever their character jumps over their opponent, even for moves done in the air. So, let's say Akuma wants to perform his Zanku Hadoken after jumping over an opponent:



Even though Akuma hasn't changed directions yet, his commands have still flipped, and his player will have to perform a quarter-circle left motion to get Akuma to perform the move. To replicate this behavior in the EXPLODsive Buffering system, we use this trigger, which is exactly same as the one was posted in the above thread:
Code:
triggerAll = (ifElse(P2dist x < 0, Facing = -1, Facing = 1) && NumExplod(DIRECTIONAL COMMAND WHEN FACING RIGHT)) || (ifElse(P2dist x < 0, Facing = 1, Facing = -1) && NumExplod(DIRECTIONAL COMMAND WHEN FACING LEFT))

This is the same code snippet used for moves done on the ground. Which makes sense, after all: ground and aerial move commands behave the same way in games that use this method.



But there are a few games that don't work like this: notably, the Marvel series (excluding the very first game in the series, X-Men: Children of the Atom) and Darkstalkers 3/Vampire Savior. Let's take a look at the latter:



Morrigan still performs her air Soul Fist with a quarter-circle right motion, even though she's passed over the opponent. This ends up being even easier to replicate:

Code:
triggerAll = ifElse(Facing = 1, NumExplod(DIRECTIONAL_COMMAND_WHEN_FACING_RIGHT), NumExplod(DIRECTIONAL_COMMAND_WHEN_FACING_LEFT))

The only thing we have to check for is the character's facing! Do note that in Marvel games, when you super jump over an opponent, your character will automatically turn around while still in the air, and the commands will reverse; this trigger will replicate that behavior too! (assuming, of course, that you've added code to make the character to turn around in the air!) But remember, this is ONLY for aerial commands; moves performed on the ground should use the standard trigger that checks P2dist x!



And in the very unlikely event you have a move that can be used in both the air and the ground, and that uses the same State for both versions, AND you want to use the Vampire Savior/Marvel method of handling air commands, use this set of triggers:

Code:
triggerAll = (ifElse(P2dist x < 0 && StateType != A, Facing = -1, Facing = 1) && NumExplod(DIRECTIONAL_COMMAND_WHEN_FACING_RIGHT)) || (ifElse(P2dist x < 0  && StateType != A, Facing = 1, Facing = -1) && NumExplod(DIRECTIONAL_COMMAND_WHEN_FACING_LEFT))

Which basically just means that we don't check P2dist x when the character is in the air. Pretty simple!



Whatever way you want your air commands to be handled, it's best if you check your source game (if the character you're making has one, of course!) to see how that game handles air commands; even if you end up deciding not to use the same behavior, it's worth thinking about why the source game chooses to have its air commands work the way they do. You might also find some inconsistencies in the way the game handles air moves! For example, in Capcom vs. SNK 2, as in most games, commands reverse directions when you jump over your opponent. But oddly enough, that doesn't happen with Iori's aerial command kick, Geshiki: Yuri Ori (a backwards kick that can only hit opponents behind Iori), which is performed by holding "back" and pressing light kick. For instance, let's say Iori starts off facing the right, and jumps over his opponent. To perform this move, the player would input "left + LK", even if they've already passed over their opponent! (in the KOF games where Iori originates, however, the player would have to input "right + LK" once they jumped over their opponent)



HOW CAPCOM HANDLES TURNING

Eagle-eyed viewers might notice that in the Vampire Savior gif above (and every gif from a Capcom game in this thread. for that matter!), Demitri doesn't start to turn around until Morrigan has passed quite a distance over his head; this is a property unique to most of Capcom's games, where the characters don't switch sides (and neither do the commands) after jumping over one another until after the character jumping over the other has traveled a certain distance far enough away from the opponent (or until they land on the ground if they never reach that distance). The exact distance a character must travel seems to vary by game (we have not extensively researched this, but SFA3 seems to be around 25, VS is around 23, and CvS2 seems to be around 12), our guess is that it's related to how crossups work.

Replicating this behavior is a bit beyond the scope of this tutorial, but to be brief: you'd add this to -2:
Code:
[State -2, AssertSpecial]
type = AssertSpecial
trigger1 = 1
flag = NoAutoTurn

;Play the standing turn anim
[State -2, ChangeAnim]
type = ChangeAnim
trigger1 = P2dist x < TURN_DISTANCE
trigger1 = ctrl
trigger1 = StateType = S
trigger1 = StateNo != 20
value = 5

;Play the crouching turn anim
[State -2, ChangeAnim]
type = ChangeAnim
trigger1 = P2dist x < TURN_DISTANCE
trigger1 = ctrl
trigger1 = StateType = C
value = 6

;When to actually turn (notice this is below the turn anim code blocks!)
[State -2, Turn]
type = Turn
trigger1 = P2dist x < TURN_DISTANCE
trigger1 = ctrl
trigger1 = StateType != A
Where "TURN_DISTANCE" is whatever the distance you want the opponent to have traveled to the left or right of your character before your character turns. If you do decide to implement this behavior, the buffer system itself will require only minor changes. You will have to use slightly different triggers than the ones we have listed here: all ChangeState triggers in State -1 that use "P2dist x < 0" will need to use something like "P2dist x < 25", or what have you. And you must replace all instances of "P2dist x < 0" in the actual buffering system itself; in the default version of the system, these only appear in the (Capcom-style) charge move block, and the guard state overrides at the very bottom of the code.
Last Edit: August 15, 2017, 03:56:18 am by Jmorphman
Re: Fix all your command issues with the EXPLODsive Buffering system!
#5  August 03, 2017, 09:53:02 pm
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
CHARGE MOVES; CAPCOM CHARGING VS. SNK CHARGING

HOW TO IMPLEMENT CHARGE MOVES

Charge moves are already fully coded into the EXPLODsive Buffering system, the only other thing that's need is for you to override your State 52, the landing state (if you have not already done so) and to add the following code to the bottom of the overriden state:

Code:
[State 52, Assert: NoWalk]
type = AssertSpecial
trigger1 = Anim != 5
flag = NoWalk
ignoreHitPause = 1

[State 52, ChangeAnim to Turning]
type = ChangeAnim
trigger1 = ctrl && P2Dist x < 0
trigger1 = Anim != 5
value = 5

[State 52, Turn]
type = Turn
trigger1 = Anim = 5 && AnimElem = 1

This will ensure that the charge buffer doesn't reset when the character lands. Other than that, adding a charge move to a character takes the exact same steps as adding a normal directional command: simply add the appropriate triggers and the right explod ID numbers, and that's it!



CHARGE MOVES IN CAPCOM GAMES COMPARED TO SNK GAMES

Capcom and SNK approach charge moves very differently; SNK bases its charge moves solely on the user's input, with no regard to character facing.  For example, let's take a look at an example of this in action:



Leona begins charging as soon as her player inputs "up-right", and continues to charge as the player keeps holding that direction (and when the player moves from "up-right" to "right"). Shortly after Leona lands, the total amount of ticks that "right" has been held will equal 45 (this total includes both "right" and "up"-right"), which means that Leona can perform her Ground Saber, so the player inputs "left + LK", and the move comes out! One other thing to note is that SNK style charge commands allow charging during hitpause. With their unique way of charging and the ability to charge during hitpauses taken together, SNK's style of charge moves makes those moves come out fairly fast and easily, befitting the fast pasted action of the KOF series.

In contrast, Capcom games—suiting the slower and more methodical pace of the SF series—"reset" their charge moves whenever the characters switch sides, and do not allow charging during hitpause. Let's take a look at how the former affects charging when a character jumps over their opponent:



Bison just delivers his standing light kick, instead of his Knee Press! His player needed to hold down "right" a lot longer than that!



It should be readily apparent just how significant it is to have charge moves reset when jumping over an opponent. Just look at how far away Bison moves just to finish charging!



The default behavior of charge moves in the EXPLODsive Buffering system follows Capcom's method, but it's fairly trivial to change it to a SNK-based method. Just replace the charge move block with the following:
Code:
;===============================<(SNK) CHARGE BACK, FORWARD>================================

;==============================================================================================
;This is somewhat similar to the Capcom style of charge moves, but we won't be keeping track of
;the "back" charge, instead, we're gonna be keeping track of how long the player has held left or
;right: when a character facing either direction holds left, var(47) will increase by one every tick.
;And when a character (again, facing any direction) holds right, var(47) will *decrease* by one every
;tick. If at any point the character stops holding the direction they were holding, var(47) will be reset
;to 0.

;So, if var(47) is greater than or equal to 45, then that means the player has sufficiently charged left
;enough to perform a charge back, forward move (provided they are facing to the right). And if var(47) is
;less than or equal to -45, the player has sufficiently charged right long enough to perform a charge back,
;forward move (if they're facing to the left).

;As with the Capcom method of charge moves, var(48) is set to 10 (Capcom has a 16 tick charge buffer SNK
;uses a 10 tick one) once var(47) has incremented (or decremented) enough. This is our charge buffer, and
;as long as it's non-zero, we are able to perform a charge move. var(48) will slowly start counting down to
;0, however, if the player stops hold left or right!
;==============================================================================================
[State -1, Left Charge Increment]
type = VarAdd
triggerAll = NumHelper(90000005) && !IsHelper
trigger1 = !AILevel
trigger1 = Helper(90000005), command = "holdback"
trigger2 = AILevel
var(47) = 1
ignoreHitPause = 1

;this will only activate if var(47) is greater than zero; this is to avoid this VarSet from activating
;when the player is charging right
[State -1, Left Charge Reset]
type = VarSet
triggerAll = var(47) > 0
triggerAll = NumHelper(90000005) && !IsHelper
trigger1 = !AILevel
trigger1 = Helper(90000005), command != "holdback"
trigger2 = AILevel
trigger2 = StateNo = 0 || (StateNo = 20 && vel x > 0) || (StateNo = [760,762])
trigger3 = AILevel
trigger3 = (StateNo = 40 && sysvar(1) != -1)
trigger4 = AILevel
trigger4 = (StateNo = [1000, 4999]) && Time = 1
var(47) = 0
ignoreHitPause = 1

[State -1, Right Charge Increment]
type = VarAdd
triggerAll = NumHelper(90000005) && !IsHelper
trigger1 = !AILevel
trigger1 = Helper(90000005), command = "holdfwd"
trigger2 = AILevel
var(47) = -1
ignoreHitPause = 1

[State -1, Right Charge Reset]
type = VarSet
triggerAll = var(47) < 0
triggerAll = NumHelper(90000005) && !IsHelper
trigger1 = !AILevel
trigger1 = Helper(90000005), command != "holdfwd"
trigger2 = AILevel
trigger2 = StateNo = 0 || (StateNo = 20 && vel x > 0) || (StateNo = [760,762])
trigger3 = AILevel
trigger3 = (StateNo = 40 && sysvar(1) != -1)
trigger4 = AILevel
trigger4 = (StateNo = [1000, 4999]) && Time = 1
var(47) = 0
ignoreHitPause = 1

[State -1, Left/Right Charge Buffer Activation]
type = VarSet
;we need to remember to check the absolute value of var(47), since in the SNK method, it can be
;either positive or negative!
trigger1 = abs(var(47)) >= 45
;var(48) is only set to 10 (as opposed to 16 like the Capcom method) here because of the different
;way the charge buffer works in SNK games; see below for a more detailed discussion of this.
var(48) = 10

[State -1, Left/Right Charge Buffer Decrement]
type = VarAdd
trigger1 = abs(var(47)) < 45 && var(48) > 0
var(48) = -1



;==============================================================================================
;Much like with Capcom-style charge moves, the length of time the command is active behaves very
;differently than with standard commands: instead of having the RemoveTime be a constant value,
;we use the current value of the charge buffer, var(48).
;==============================================================================================

;=================================<(SNK) CHARGE LEFT, RIGHT>===================================
;we only need one command explod for this move, since we already know that the player has been
;inputting the opposite direction!
[State -1, Charge Left, Right: Right]
type = Explod
triggerAll = var(48) > 0
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = Helper(90000005), command = "fwd" && Helper(90000005), command != "back" && Helper(90000005), command != "up" && Helper(90000005), command != "down"
anim = 1
ID = 90010606
removeTime = var(48) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
pauseMoveTime = var(48) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
superMoveTime = var(48) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
ignoreHitPause = 1



;=================================<(SNK) CHARGE RIGHT, LEFT>===================================
[State -1, Charge Right, Left: Left]
type = Explod
triggerAll = var(48) > 0
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = Helper(90000005), command = "back" && Helper(90000005), command != "fwd" && Helper(90000005), command != "up" && Helper(90000005), command != "down"
anim = 1
ID = 90010704
removeTime = var(48) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
pauseMoveTime = var(48) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
superMoveTime = var(48) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
ignoreHitPause = 1



;==============================================================================================
;=========================<(SNK) CHARGE BACK, FORWARD, BACK, FORWARD>==========================
;==============================================================================================

;==============================================================================================
;This is extremely similar to the QCFx2/QCBx2 code, only with slight adjustments made to make it
;fit the format of a charge move. Also note that opposed to the way the standard/special charge
;command works, you're given a constant 10 ticks of input window between each command, instead of
;having to complete the entire move before var(48) ticks down to zero.
;==============================================================================================

;===========================<(SNK) CHARGE LEFT, RIGHT, LEFT, RIGHT>============================

[State -1, Charge Left, Right, Left, Right: 1st Right]
type = Explod
triggerAll = var(48) > 0
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = Helper(90000005), command = "fwd" && Helper(90000005), command != "back" && Helper(90000005), command != "up" && Helper(90000005), command != "down"
anim = 1
ID = 90030606
removeTime = 10
pauseMoveTime = 10
superMoveTime = 10
ignoreHitPause = 1

[State -1, Charge Left, Right, Left, Right: 2nd Left]
type = Explod
triggerAll = NumExplod(90030606)
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = Helper(90000005), command = "back" && Helper(90000005), command != "fwd" && Helper(90000005), command != "up" && Helper(90000005), command != "down"
anim = 1
ID = 90030604
removeTime = 10
pauseMoveTime = 10
superMoveTime = 10
ignoreHitPause = 1

[State -1, Charge Left, Right, Left, Right: 2nd Right]
type = Explod
triggerAll = NumExplod(90030604)
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = Helper(90000005), command = "fwd" && Helper(90000005), command != "back" && Helper(90000005), command != "up" && Helper(90000005), command != "down"
anim = 1
ID = 90030616
removeTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
pauseMoveTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
superMoveTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
ignoreHitPause = 1



;===========================<(SNK) CHARGE RIGHT, LEFT, RIGHT, LEFT>============================
[State -1, Charge Right, Left, Right, Left: 1st Left]
type = Explod
triggerAll = var(48) > 0
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = Helper(90000005), command = "back" && Helper(90000005), command != "fwd" && Helper(90000005), command != "up" && Helper(90000005), command != "down"
anim = 1
ID = 90030704
removeTime = 10
pauseMoveTime = 10
superMoveTime = 10
ignoreHitPause = 1

[State -1, Charge Right, Left, Right, Left: 2nd Right]
type = Explod
triggerAll = NumExplod(90030704)
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = Helper(90000005), command = "fwd" && Helper(90000005), command != "back" && Helper(90000005), command != "up" && Helper(90000005), command != "down"
anim = 1
ID = 90030706
removeTime = 10
pauseMoveTime = 10
superMoveTime = 10
ignoreHitPause = 1

[State -1, Charge Right, Left, Right, Left: 2nd Left]
type = Explod
triggerAll = NumExplod(90030706)
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = Helper(90000005), command = "back" && Helper(90000005), command != "fwd" && Helper(90000005), command != "up" && Helper(90000005), command != "down"
anim = 1
ID = 90030714
removeTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
pauseMoveTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
superMoveTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
ignoreHitPause = 1



;==============================================================================================
;===================================<(SNK) CHARGE DOWN, UP>====================================
;==============================================================================================

;==============================================================================================
;This uses the exact same concept as the Back, Forward charge move, only things are much easier
;since we don't have to deal with switching sides and having to have mirrored commands!

;Do note that we use different variables [var(49) and var(50)] here.

;The only differences between this and the Capcom method are that var(50) is set to 10, not 16.
;==============================================================================================

[State -1, Down Charge Increment]
type = VarAdd
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !AIlevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command = "holddown"
trigger2 = !AIlevel
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command = "holdup"
trigger3 = AIlevel
var(49) = 1

[State -1, Down Charge Reset]
type = VarSet
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !AILevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command != "holddown"
trigger2 = !AILevel
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command != "holdup"
trigger3 = AILevel
trigger3 = StateNo = 0 || StateNo = 20 || (StateNo = 40 && Time = 1) || StateNo = 760
trigger4 = AILevel
trigger4 = (StateNo = [1000,4999]) && Time = 1
trigger5 = AILevel
trigger5 = (StateNo = [120,155]) && StateType != C
var(49) = 0

[State -1, Down Charge Buffer Activation]
type = VarSet
trigger1 = var(49) >= 45
var(50) = 10

[State -1, Down Charge Buffer Decrement]
type = VarAdd
trigger1 = var(49) < 45 && var(50) > 0
var(50) = -1

[State -1, Charge Down, Up: Up]
type = Explod
triggerAll = var(50) > 0
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command = "up" && Helper(90000005), command != "fwd" && Helper(90000005), command != "back" && Helper(90000005), command != "down"
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command = "down" && Helper(90000005), command != "fwd" && Helper(90000005), command != "back" && Helper(90000005), command != "up"
anim = 1
ID = 90010808
removeTime = var(50) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
pauseMoveTime = var(50) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
superMoveTime = var(50) + ifElse(HitPauseTime, HitPauseTime - 1, 0)
ignoreHitPause = 1



;==============================================================================================
;================<(SNK) CHARGE DOWN-BACK, DOWN-FORWARD, DOWN-BACK, UP-FORWARD>=================
;==============================================================================================

;==============================================================================================
;Almost identical to the charge Back, Forward, Back, Forward command, only we have to check var(50)
;(to make sure down has been charged enough) in addition to checking var(48) (to make sure back has
;been charged enough.

;As with the SNK version of the charge Back, Forward, Back, Forward command, you're given a constant
;10 ticks of input window between each command, instead of having to complete the entire move before
;var(50) ticks down to zero.
;==============================================================================================

;==================<(SNK) CHARGE DOWN-LEFT, DOWN-RIGHT, DOWN-LEFT, UP-RIGHT>===================
[State -1, Charge Down-Left, Down-Right, Down-Left, Up-Right: Down-Right]
type = Explod
triggerAll = var(48) > 0 && var(50) > 0
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command = "dfwd" && Helper(90000005), command != "back" && Helper(90000005), command != "up"
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command = "ufwd" && Helper(90000005), command != "back" && Helper(90000005), command != "down"
anim = 1
ID = 90030803
removeTime = 10
pauseMoveTime = 10
superMoveTime = 10
ignoreHitPause = 1

[State -1, Charge Down-Left, Down-Right, Down-Left, Up-Right: 2nd Down-Left]
type = Explod
triggerAll = NumExplod(90030803)
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command = "dback" && Helper(90000005), command != "fwd" && Helper(90000005), command != "up"
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command = "uback" && Helper(90000005), command != "fwd" && Helper(90000005), command != "down"
anim = 1
ID = 90030801
removeTime = 10
pauseMoveTime = 10
superMoveTime = 10
ignoreHitPause = 1

[State -1, Charge Down-Left, Down-Right, Down-Left, Up-Right: Up-Right]
type = Explod
triggerAll = NumExplod(90030801)
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command = "ufwd" && Helper(90000005), command != "back" && Helper(90000005), command != "down"
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command = "dfwd" && Helper(90000005), command != "back" && Helper(90000005), command != "up"
anim = 1
ID = 90030809
removeTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
pauseMoveTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
superMoveTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
ignoreHitPause = 1



;==================<(SNK) CHARGE DOWN-RIGHT, DOWN-LEFT, DOWN-RIGHT, UP-LEFT>===================
[State -1, Charge Down-Right, Down-Left, Down-Right, Up-Left: Down-Left]
type = Explod
triggerAll = var(48) > 0 && var(50) > 0
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command = "dback" && Helper(90000005), command != "fwd" && Helper(90000005), command != "up"
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command = "uback" && Helper(90000005), command != "fwd" && Helper(90000005), command != "down"
anim = 1
ID = 90030901
removeTime = 10
pauseMoveTime = 10
superMoveTime = 10
ignoreHitPause = 1

[State -1, Charge Down-Right, Down-Left, Down-Right, Up-Left: 2nd Down-Right]
type = Explod
triggerAll = NumExplod(90030901)
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command = "dfwd" && Helper(90000005), command != "back" && Helper(90000005), command != "up"
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command = "ufwd" && Helper(90000005), command != "back" && Helper(90000005), command != "down"
anim = 1
ID = 90030903
removeTime = 10
pauseMoveTime = 10
superMoveTime = 10
ignoreHitPause = 1

[State -1, Charge Down-Right, Down-Left, Down-Right, Up-Left: Up-Left]
type = Explod
triggerAll = NumExplod(90030903)
triggerAll = NumHelper(90000005) && !IsHelper && !AILevel
trigger1 = !NumExplod(92828282) && !NumExplod(92468246)
trigger1 = Helper(90000005), command = "uback" && Helper(90000005), command != "fwd" && Helper(90000005), command != "down"
trigger2 = NumExplod(92828282) || NumExplod(92468246)
trigger2 = Helper(90000005), command = "dback" && Helper(90000005), command != "fwd" && Helper(90000005), command != "up"
anim = 1
ID = 90030907
removeTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
pauseMoveTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
superMoveTime = 10 + ifElse(HitPauseTime, HitPauseTime - 1, 0)
ignoreHitPause = 1
Last Edit: August 15, 2017, 03:27:08 am by Jmorphman
Re: Fix all your command issues with the EXPLODsive Buffering system!
#6  August 03, 2017, 09:53:13 pm
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
ADVANCED: HOW TO ADD NEW COMMANDS AND CUSTOMIZE THE SYSTEM

Before digging deep into customizing the system, it's best to first get a good understanding of how it all works. It's highly recommended that you at least glance at the EXPLODsive Buffering system's code and read some of the comments contained therein before continuing this section.



THE EXPLOD ID NUMBER FORMAT

Let's first review how we've defined the various explod ID's, and the best way to approach making up new ones. Every explod ID is an eight-digit number that all begin with "9", in order to avoid overlapping with any existing character's explod ID's. For all directional inputs, each ID starts with "900", followed by a four digit identifier code, and then a final digit representing the directional input (expressed in the number pad format used by several fighting games, where each direction is represented by a number on a computer's numpad: 2 would be down, 3 is down-right, 6 is right, 5 is neutral, etc.). These identifier codes correspond to one specific command, and are designed so that anyone can look at any individual explod ID and know which command it belongs to. Do note that the right-facing and left-facing motions that make up the same command (for example, quarter-circle right and quarter-circle left make up the "quarter-circle" command) have separate ID's! Also, for release inputs, we precede the final digit with 5 (so a "release down" input would be 900aaa52, with "aaa" being the four digit identifier code [one digit of which has been replaced by 5, but that's OK, since the codes all end in zero anyways!]); similarly, if a single command repeats an input, we precede the second input's final digit with 1 (so if a particular command had two separate "down" inputs, the second would be 900aaa12).

Button commands, however, all start with "90000", followed by three digits that corresponds to a StateNo of a normal attack that the button in question is most commonly associated with. So "x" is assigned to "200", since that's what most authors use as their light punch state. "y" gets "210", z gets "220" and so on. And "start" gets 195, since it's the state used most commonly for taunts! Button release commands have "5" as their final digit, rather than "0" ("release start" ends with a "6", since "5" was already taken).

For button press commands (such as Akuma's Shun Goku Satsu), we combine the two approaches: they take the form "9aaaaBBB", with "aaaa" being a four-digit identifier code (much like directional releases) and "BBB" being the StateNo that particular button input is most commonly associated with (as with the regular button explods). And for any directional inputs in the button press command, it would look like "9aaaa00#", with "#" being directional input expressed in the numpad format.

Finally, the directional input explods are simply "9000000" followed by a single digit that represents their direction, expressed in the numpad format (so the down directional input explod has an ID of "90000002").

But these are all just guidelines; you are free to totally disregard the format we used!



BASIC DIRECTIONAL INPUTS

There are eight explods that are defined at the very beginning of the code block that makes up the EXPLODsive Buffering system, each representing a specific directional input. These are required for the system to function; they are active whenever the specified direction is currently being input, with an infinite RemoveTime, and disappear as soon as they are released (via a series of RemoveExplods at the bottom of the Buffering system code block). They are used by the system to detect directional releases, but can also be used outside of the Buffering system code block to detect when a specific direction is being input or held down.

Unless you want to drastically modify the system in order to replicate the behavior of games that require every individual directional input to be held for at least 2 or more ticks, you shouldn't be messing with these.



BUTTONS

These are all pretty simple: there are explods for both button presses, and releases; both get activated whenever the button they represent is input (or released), and have a RemoveTime of 3. Button press explods willpersist through HitPause (by adding " + ifElse(HitPauseTime, HitPauseTime - 1, 0)" to the end of the RemoveTime. This will also be used in the final directional input of any system, special, or super command), but button releases will not—in keeping with how commercial games handle this.

There are also a series of button release detectors that work much in the same way that the directional release detectors work, but unlike those directiona release detector explods, it isn't really worth the trouble of using the button release detector explods in place of MUGEN's standard "holda"/"holdx"/etc. Just use the standard hold button commands that MUGEN uses!



RELEASE INPUTS

The vast majority of all commands in fighting games allow the first input of any command to be a release input, so that you're allowed to hold the direction that the command starts with for however long you want, then input the rest of the command, and still get the move. This is why in MUGEN, people typically write all their character's commands as starting with a release input; however, things are a bit more complicated outside of MUGEN (and thus, more complicated in this buffer too). In most games, the window of time a player has to input the next command in sequence after performing a release input is shorter than the window of time they have to input the next command after a press input. For example, in Street Fighter III, if you want to perform a quarter-circle forward motion, and you press (or hold) the "down" direction, you will have 10 ticks to input "down-forward" and continue the command. But if instead you were to hold the "down" direction down for a few seconds before releasing it, you will have only 5 ticks to input "down-forward" to continue entering the command.

The way this is translated to the EXPLODsive Buffering system is this: we have two separate explods for the first input of every command that uses a release directional input. One explod is created whenever the first input is entered, and has a RemoveTime of 10 (which is the same amount of time that most other directional explods use in this system); one other explod is also created whenever the direction of the first input is released: this has a RemoveTime of 5, which is generally what commercial fighting games use.

Do note that not every game gives players the same input windows for every single command: for example, in Street Fighter Alpha 3 (on normal speed), press directional inputs all have a 10 tick input window, but the input window for the release directional input in a quarter-circle motion is 6, while the input window for the release directional input in a forward, down, down-forward motion is only 4! There's a whole bunch of variance here, sometimes in the same game, and so it's best to check the source game of the character you're making (if applicable) to try and get an idea of what RemoveTime will be best for each of your commands.



LINKED COMMANDS

As mentioned in one of the above posts, some commands are "linked", meaning they are partially dependent on a command that came before it. For example, the double quarter-circle command is dependent on quarter-circle command, and it uses most of the explods defined for that quarter-circle command. The final input from the command that the linked command is descended from needs to be repeated, however, with it's own, unique ID. This is because the final explod of earlier command will persist through hitpause, which is not true of the input in that linked command: otherwise (to use the quarter-circle example again), you'd be able to input a QCF during a long hitpause, wait for the hitpause to end, and then input another QCF and still get a QCFx2 motion, and that's not how things are supposed to work!

Also, do note that Capcom games use a command shortcut for QCFx2/QCBx2 motions; a D, DF, F, D, DF will work just as well as a D, DF, F, D, DF, F motion; if you want to replicate this behavior, all you have to do is check to see if either the repsective "DF/DB" or the "F/B" explods exist in the triggers for that move's ChangeState.



SNK HALF-CIRCLE MOTIONS

For half-circle motions, SNK only checks for the cardinal inputs (i.e., the non-diagonal directions) to see if the user has successfully complated the move, and gives those cardinal inputs an extra-long input window (in KOF '98, for example, it's 16, compared to the 10 tick input window for normal commands) to compensate for the loss of the diagonal inputs.

For the default EXPLODsive Buffering system, however, we have opted to allow diagonal inputs in these SNK-style half-circle commands, but to make them optional (because, to be quite honest, ignoring diagonal inputs entirely is pretty dumb!). What this ends up looking like is that the command can be completed only using cardinal inputs, but any diagonal inputs will still be checked: any diagonal input will keep the command "alive" even if the cardinal input preceeding it has exhausted its input window. Capcom's 360/720 motions also use this behavior of ignoring all diagonal inputs, and we have likewise adapted them in the same fashion that we have SNK half-circle motions.

The way that translates to this explod system is that (for half-circle motions) we allow the user to skip the diagonal inputs: the "down" explod in a HCF command could be created if the "back" explod exists, e.g., instead of requiring the "down-back" explod to exist. However, we must also allow the "back" explod to be created if the "down-back" explod exists: this makes it so the diagonal inputs aren't just a waste of time; they will keep the command alive even if the previous cardinal direction explod has expired, which is exactly the same behavior that KOF has.

To replicate the exact same behavior that SNK games use, simply delete the diagonal input explods, and increase the RemoveTime on each of the cardinal input explods.



CHARGE MOVES

As mentioned in an earlier post, Capcom and SNK approach charge moves very differently; SNK bases its charge moves solely on the user's input, with no regard to character facing. Capcom games, however, "reset" their charge moves whenever the characters switch sides. Additionally, SNK style charge commands allow charging during hitpause; Capcom does not allow this.

The default behavior of charge moves in the EXPLODsive Buffering system are patterned after Capcom's style of charge moves; for the SNK style, please see that earlier post, where code that replicates SNK behavior is provided, as well as further discussion about the nuances of Capcom and SNK's style of charge moves.

The system uses four different variables—var(47), var(48), var(49), and var(50)—to record how long the player has held a specific direction, and how long a charge move is available. In the default (Capcom) system, when a right-facing character holds left, or a left-facing character holds right, var(47) will increase by one every tick. If at any point the character stops holding that respective direction, var(47) will be reset to 0. And if var(47) has a value greater than or equal to 45 (meaning left or right has been held for 45 or more ticks), then var(48) is set to 48. This is our charge buffer, and as long as it's non-zero, the character is able to perform a charge move. var(48) will slowly start counting down to 0, however, if the player stops hold left or right.

With Capcom-style charge moves, the length of time the command is active behaves very differently than with standard commands, instead of having the RemoveTime be a constant value, we use the current value of the charge buffer, var(48). With SNK-style charge moves, this only applies to the standard/special charge command ([charge] back, forward / [charge] down, up), and not the super commands ([charge] back, forward, back, forward / [charge] down-back, down-forward, down-back, up-forward).

"(charge) down, up" commands work much in the same way, only things are much easier since we don't have to deal with switching sides and having to have mirrored commands! Additionally, var(49) is used instead of var(47), and var(50) is used instead of var(48).



OVERRIDING GUARD STATES

The default EXPLODsive Buffering system overrides MUGEN's default guard system whenever the character is airborne, in order to add our own triggers that determine whether the character should enter an air block state. This will ensure that the player can always block as long as they are holding a direction away from the opponent. This block of code also adds "chicken guarding", which is allowing the player to air guard by holding not only back, but down-back and up-back.

An additional, optional step that we recommend you to take is to override the guard states (120, 130, 131, 132, 140, 150, 151, 152, 154, 155) with an updated version that uses the EXPLODsive buffering system. A copy of those fixed states can be found here. Do note the commented out code at the bottom of State 130: there's a PlaySnd and an Explod sctrl that play a landing sound and dust effect if the player was guarding in the air and has landed on the ground while still guarding. This isn't present in Kung Fu Man, but it is in Jmorphman's character's and other PotS style creations. If you end up using either sctrl, make sure the sound/explod animation they're referencing exists in your own character!



CUSTOMIZING THE SYSTEM

Customizing the existing moves is pretty easy, and primarily takes the form of adjusting the RemoveTime's to make an individual input less or more lenient (or you can make the same adjustments to all the inputs in an individual command, or even apply those changes to every input in the system!). The system is flexible enough to do pretty much whatever you like!



ADDING NEW MOVES

Once you get the format down, making a new command is pretty easy: start off with a press explod and a release explod for the first input, then continue through each individual input. You must make absolutely sure that the explods have the right command triggers associated with them, and that they have a unique ID (based on the same format described above! remember to use a unique identifier code, one that hasn't been used before in the list of explod ID's!) that is used to activate the next command in sequence. And if you have any questions or problem, feel free to ask them here!



MASHING BUTTON MOVES

These are best approached with variables (which is how most creators have approached them in the past), not with the EXPLODsive system. You might want to use a button explod in the trigger that activates the move, though, so we'll do a brief overview of how to approach things. Here's an example of how mashing moves are typically done:
Code:
;var(46) functions as a timer; as long as it's greater than zero, the mashing move will continue (if the character is performing that move)
[State -2, Mash Punch Timer Decrement]
type = VarAdd
trigger1 = var(46) > 0
var(46) = -1

;var(45) counts how many times one of the punch buttons has been pressed, and is reset when the mash punch timer is zero.
[State -2, Mash Punch Counter Reset]
type = VarSet
trigger1 = var(46) <= 0
trigger2 = Time = 1
;this should be whatever the StateNo is of the mashing move
trigger2 = StateNo = 1000
var(45) = 0

;this increases both var(45) and var(46): each time a punch button is input, var(45) increases by one. var(46) also increases whenever this happens
;but by differing ammounts, based on what strength of punch was input. This makes it so that it is harder to activate a mash type move with heavier
;attacks, which is how Capcom styles it's charge moves. To make this follow SNK rules, simply make var(46) increase by the same amount for every
;input.
[State -2, Mash Punch Counter+Timer Increment]
type = VarAdd
trigger1 = command = "x"
trigger1 = var(46) := 14
trigger2 = command = "y"
trigger2 = var(46) := 12
trigger3 = command = "z"
trigger3 = var(46) := 10
trigger4 = command = "pp"
trigger4 = var(46) := 14
var(45) = 1
This stuff would all go in -2. Do note that we're not using the button explods here; those last for three ticks, so we use MUGEN's standard command system for the buttons, which will persist for only one tick.

The actual mashing move State will be totally up to you to determine how it should work, but this should come at the end:
Code:
[State 1000, End]
type = ChangeState
trigger1 = var(46) <= 0
value = 1001
Because when var(46)/the timer runs out, the move should end!

As for the ChangeState in -1, here's an extremely simplified version of what a mashing move ChangeState would look like:
Code:
[State -1, Mashing Punch move]
type = ChangeState
value = 1000
triggerAll = !AILevel
triggerAll = var(45) >= 5
triggerAll = NumExplod(90000200) || NumExplod(90000210) || NumExplod(90000220)
triggerAll = RoundState = 2 && StateType != A
trigger1 = ctrl
The only thing different from other ChangeStates is that we're checking var(45). We have "var(45) >= 5" here, but it's up to you how many times the buttons need to be tapped to make the move activate! Also note that here we are using explods; this will allow the mashing move to be active longer than just one tick. You could also make a separate set of button explods with different RemoveTimes than the regular, standard button explods.
Last Edit: August 15, 2017, 03:28:56 am by Jmorphman
Re: Fix all your command issues with the EXPLODsive Buffering system!
#7  August 03, 2017, 10:39:50 pm
  • ****
  • Let's go :3

  • Online
You guys are fucking awesome,thanks for the huge and detailed write up. Gonna try this out right now on KFM to learn the ropes. thanks again.
Re: Fix all your command issues with the EXPLODsive Buffering system!
#8  August 10, 2017, 09:50:09 pm
  • ****
  • High AF
    • USA
    • Skype - DJHANNIBALROYCEUSA
    • sites.google.com/site/djhrmugen/
im ready to add all of this to my chars
WiiU Username: DJHANNIBALROYCE7
Re: Fix all your command issues with the EXPLODsive Buffering system!
#9  August 10, 2017, 11:09:33 pm
  • ******
    • www.justnopoint.com/
If you have any questions or need new commands made ask me or JMM. We update the .txt file when we make new commands to help maintain a standard. We will make any command you need :)

@jmorphman: I think it'd be useful to put a link to that txt file in the 1st post under table of contents. Maybe put a link to KFM, King, and any other chars you've converted.
Last Edit: August 10, 2017, 11:54:22 pm by Just No Point
Re: Fix all your command issues with the EXPLODsive Buffering system!
#10  August 11, 2017, 04:09:05 am
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
done

also added some stuff to the advanced section about overriding guard states
Last Edit: August 11, 2017, 04:50:06 am by Jmorphman
Re: Fix all your command issues with the EXPLODsive Buffering system!
#11  August 15, 2017, 02:42:13 am
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
We are currently updating the system to add support for attacks that cause a reversed command status effect (you have just left/right reverse, or just up/down reverse, or have both at the same time!) and also support for tagging; stay tuned!
Re: Fix all your command issues with the EXPLODsive Buffering system!
#12  August 15, 2017, 03:15:45 am
  • ***
    • USA
    • lndnrivers@yahoo.com
I was wondering if you guys could add the D,D motion command since I don't see it in the txt file.
Re: Fix all your command issues with the EXPLODsive Buffering system!
#13  August 15, 2017, 03:19:25 am
  • *****
  • I am the enemy. I will succeed.
    • USA
I see it twice
Now listen all you boys and girls,
All around the world,
Don't be afraid to get up and move!
You know that we're all Super Stars,
We're the ones that made it this far!
Put a smile on that face,
There's no time to waste,
Oh, let's do The Odyssey!
Re: Fix all your command issues with the EXPLODsive Buffering system!
#14  August 15, 2017, 03:36:38 am
  • ***
    • USA
    • lndnrivers@yahoo.com
Speedpreacher I know what you are talking about but the D,D motion command is not under the Directional command section. I am trying to figure out how to add this command to JMM King since I want one of her specials to have the D,D motion.
Last Edit: August 15, 2017, 03:44:20 am by Shadic12
Re: Fix all your command issues with the EXPLODsive Buffering system!
#15  August 15, 2017, 03:43:45 am
  • ******
  • I drank all the throwing wine!!!
    • USA
    • network.mugenguild.com/jmorphman
90000562 is "D, D"; but this only lasts for 3 ticks; useful as like an air dash command or something.

90012602 is also "D, D"; this is the special variant, it lasts the same amount of time as all the other special moves do.

And we also have a "D, D, D" command: 90032602!
Re: Fix all your command issues with the EXPLODsive Buffering system!
New #16  August 15, 2017, 03:46:08 am
  • ***
    • USA
    • lndnrivers@yahoo.com
Thanks for clearing that up for me JMM.
Last Edit: August 15, 2017, 06:57:58 am by Shadic12