Second part, the functions
// Beginning of functions
FUNCTION sfPadInitialise: padnumber%, specials% = 0, maxmoves% = 0
LOCAL p AS allpad
p.sfjoypad = padnumber-1 // joypads 1 to 10 are set as 0 to 9
p.sfdirection = 9 // 9 is always "no direction". 0 should never be seen.
p.sffire = 0
p.sfjump = 0
p.sfspecial = specials
p.sfmaxmove = maxmoves
IF specials = 0
// do nothing as no special moves need to be defined. sfPad[] will still show how long fire and/or jump have been pressed however
ELSE
REDIM p.sfmoves[specials][maxmoves][2]
REDIM p.sftypemove[specials]
REDIM p.sfmovestate[specials]
REDIM p.sfmovecounter[specials]
REDIM p.sfmovetolerance[specials]
REDIM p.sfendmove[specials]
REDIM p.sftolerance[specials]
REDIM p.sfmovewait[specials]
REDIM p.sfcanignorefire[specials]
ENDIF
DIMPUSH sfPad[], p // add the new joypad configuration
LOCAL padconfig_id
padconfig_id = LEN(sfPad[]) - 1
RETURN padconfig_id // return the Pad ID number. Necessary for keeping track of which pad is set which control scheme
ENDFUNCTION
FUNCTION sfPadAddMove: move$, padnumber, move
IF move$="right dash"
sfPad[padnumber].sftypemove[move]=1 // its a step by step kind of special move.
sfPad[padnumber].sfmovestate[move]=0 // clear registers
sfPad[padnumber].sfmovecounter[move]=0 // clear registers
sfPad[padnumber].sfmovetolerance[move]=0 // clear registers
sfPad[padnumber].sfmoves[move][0][0]=9 // first direction is no direction. player must be still
sfPad[padnumber].sfmoves[move][0][1]=0 // no fire or jump needed. fire = 1. jump = 2. fire & jump = 3
sfPad[padnumber].sfmoves[move][1][0]=2 // move right
sfPad[padnumber].sfmoves[move][1][1]=0 // no fire or jump
sfPad[padnumber].sfmoves[move][2][0]=9 // don't move
sfPad[padnumber].sfmoves[move][2][1]=0 // no fire or jump
sfPad[padnumber].sfmoves[move][3][0]=2 // move right again
sfPad[padnumber].sfmoves[move][3][1]=0 // no fire or jump
sfPad[padnumber].sfendmove[move]=4 // if player reaches step 4 they have successfully executed the dash to the right
sfPad[padnumber].sfcanignorefire[move]=1 // fire status can be ignored. You can still charge your laser
sfPad[padnumber].sftolerance[move]=0 // tolerate NO accidental joypad slips. If you can't double press right in quick succession, there is something wrong with you.
sfPad[padnumber].sfmovewait[move]=8 // tolerate 8 fps between changes of step
ENDIF
IF move$="left dash"
sfPad[padnumber].sftypemove[move]=1
sfPad[padnumber].sfmovestate[move]=0
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
sfPad[padnumber].sfmoves[move][0][0]=9
sfPad[padnumber].sfmoves[move][0][1]=0
sfPad[padnumber].sfmoves[move][1][0]=6
sfPad[padnumber].sfmoves[move][1][1]=0
sfPad[padnumber].sfmoves[move][2][0]=9
sfPad[padnumber].sfmoves[move][2][1]=0
sfPad[padnumber].sfmoves[move][3][0]=6
sfPad[padnumber].sfmoves[move][3][1]=0
sfPad[padnumber].sfendmove[move]=4
sfPad[padnumber].sfcanignorefire[move]=1
sfPad[padnumber].sftolerance[move]=0
sfPad[padnumber].sfmovewait[move]=8
ENDIF
IF move$="right dragonpunch"
sfPad[padnumber].sftypemove[move]=1
sfPad[padnumber].sfmovestate[move]=0
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
sfPad[padnumber].sfmoves[move][0][0]=2
sfPad[padnumber].sfmoves[move][0][1]=0
sfPad[padnumber].sfmoves[move][1][0]=4
sfPad[padnumber].sfmoves[move][1][1]=0
sfPad[padnumber].sfmoves[move][2][0]=3
sfPad[padnumber].sfmoves[move][2][1]=0
sfPad[padnumber].sfmoves[move][3][0]=2
sfPad[padnumber].sfmoves[move][3][1]=1
sfPad[padnumber].sfendmove[move]=4
sfPad[padnumber].sfcanignorefire[move]=0 // you cannot press fire until the right time (the end of the move) or the step by step sequence is void
sfPad[padnumber].sftolerance[move]=8
sfPad[padnumber].sfmovewait[move]=12 // tolerate 12 fps between changes of step. Very tolerant. Typical real world gameplay would be 8
ENDIF
IF move$="right dragonpunch slip" // Just added so dragonpunch is easier to perform. My gf kept messing up and complaining so I added this to make it easier to trigger
sfPad[padnumber].sftypemove[move]=1
sfPad[padnumber].sfmovestate[move]=0
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
sfPad[padnumber].sfmoves[move][0][0]=2
sfPad[padnumber].sfmoves[move][0][1]=0
sfPad[padnumber].sfmoves[move][1][0]=9
sfPad[padnumber].sfmoves[move][1][1]=0
sfPad[padnumber].sfmoves[move][2][0]=3
sfPad[padnumber].sfmoves[move][2][1]=0
sfPad[padnumber].sfmoves[move][3][0]=2
sfPad[padnumber].sfmoves[move][3][1]=1
sfPad[padnumber].sfendmove[move]=4
sfPad[padnumber].sfcanignorefire[move]=0
sfPad[padnumber].sftolerance[move]=8
sfPad[padnumber].sfmovewait[move]=12
ENDIF
IF move$="left dragonpunch"
sfPad[padnumber].sftypemove[move]=1
sfPad[padnumber].sfmovestate[move]=0
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
sfPad[padnumber].sfmoves[move][0][0]=6
sfPad[padnumber].sfmoves[move][0][1]=0
sfPad[padnumber].sfmoves[move][1][0]=4
sfPad[padnumber].sfmoves[move][1][1]=0
sfPad[padnumber].sfmoves[move][2][0]=5
sfPad[padnumber].sfmoves[move][2][1]=0
sfPad[padnumber].sfmoves[move][3][0]=6
sfPad[padnumber].sfmoves[move][3][1]=1
sfPad[padnumber].sfendmove[move]=4
sfPad[padnumber].sfcanignorefire[move]=0
sfPad[padnumber].sftolerance[move]=8
sfPad[padnumber].sfmovewait[move]=12
ENDIF
IF move$="left dragonpunch slip"
sfPad[padnumber].sftypemove[move]=1
sfPad[padnumber].sfmovestate[move]=0
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
sfPad[padnumber].sfmoves[move][0][0]=6
sfPad[padnumber].sfmoves[move][0][1]=0
sfPad[padnumber].sfmoves[move][1][0]=9
sfPad[padnumber].sfmoves[move][1][1]=0
sfPad[padnumber].sfmoves[move][2][0]=5
sfPad[padnumber].sfmoves[move][2][1]=0
sfPad[padnumber].sfmoves[move][3][0]=6
sfPad[padnumber].sfmoves[move][3][1]=1
sfPad[padnumber].sfendmove[move]=4
sfPad[padnumber].sfcanignorefire[move]=0
sfPad[padnumber].sftolerance[move]=8
sfPad[padnumber].sfmovewait[move]=12
ENDIF
IF move$="fast fire"
sfPad[padnumber].sftypemove[move]=2 // This move is rapid dire, triggered when player hits fire quickly
sfPad[padnumber].sfmovestate[move]=0 // In this case, how many times has player hit fire quickly?
sfPad[padnumber].sfmovecounter[move]=0 // How long has fire button not been pressed?
sfPad[padnumber].sfmovetolerance[move]=0 // last count was fire? then 32. last count was not fire? then 64.
sfPad[padnumber].sfendmove[move]=0 // This is set to 255 when fast fire special move is activated
sfPad[padnumber].sfcanignorefire[move]=6 // How many times must player hit fire before special move is activated?
sfPad[padnumber].sftolerance[move]=6 // How long can fire be pressed for before you are too slow to activate move
sfPad[padnumber].sfmovewait[move]=6 // How long can a break between fire last before you are too slow to activate move
ENDIF
ENDFUNCTION
FUNCTION sfPadDefaultControls: padnumber
sfPad[padnumber].firebutton = 0 // fire button is first joypad button - only used if GLBasic is in full version
sfPad[padnumber].jumpbutton = 1 // jump button is second joypad button - only used is GLBasic is in full version
sfPad[padnumber].keyright = 205 // right arrow
sfPad[padnumber].keydown = 208 // down arrow
sfPad[padnumber].keyleft = 203 // left arrow
sfPad[padnumber].keyup = 200 // up arrow
sfPad[padnumber].keyfire = 29 // left CTRL
sfPad[padnumber].keyjump = 56 // Left ALT
ENDFUNCTION
// The main routine for checking joypad state and status of special moves
FUNCTION sfPadState: padnumber%, cancelspecials% = FALSE, cancelcharge% = FALSE
LOCAL isfire% = FALSE; LOCAL isjump% = FALSE; LOCAL isblock% = FALSE; LOCAL ismagic% = FALSE; LOCAL isup% = FALSE; LOCAL isdown% = FALSE; LOCAL isleft% = FALSE; LOCAL isright% = FALSE; LOCAL jumpfire% = 0
LOCAL jx = 0; LOCAL jy = 0; LOCAL ba = 0; LOCAL bb = 0; LOCAL bc = 0; LOCAL bd = 0
LOCAL state% = 0; LOCAL checkmove% = 0; LOCAL checkbutton% = 0; LOCAL checknextmove% = 0; LOCAL checknextbutton% = 0
// Read joypad & keyboard
JOYSTATE jx, jy, ba, bb // Works with trial version. Can't use more than one joypad! Tested with keyboard as second input and that works.
// If you have the full version of GLBasic then you can enable the following two lines of code and remove the line above
// Theoretically this routine could support up to 10 joypads, all with their own set of special moves.
// jx = GETJOYX(sfPad[padnumber].sfjoypad); jy = GETJOYY(sfPad[padnumber].sfjoypad)
// ba = GETJOYBUTTON(sfPad[padnumber].sfjoypad,sfPad[padnumber].firebutton); bb = GETJOYBUTTON(sfPad[padnumber].sfjoypad,sfPad[padnumber].jumpbutton)
IF KEY(sfPad[padnumber].keyfire) = 1 OR ba > 0
isfire = TRUE
jumpfire = jumpfire +1
ENDIF
IF KEY(sfPad[padnumber].keyjump) = 1 OR bb > 0
isjump = TRUE
jumpfire = jumpfire +2
ENDIF
IF KEY(sfPad[padnumber].keyup) = 1 OR jy < -0.8
isup = TRUE
ELSEIF KEY(sfPad[padnumber].keydown) = 1 OR jy > 0.8
isdown = TRUE
ENDIF
IF KEY(sfPad[padnumber].keyleft) = 1 OR jx < -0.8
isleft = TRUE
ELSEIF KEY(sfPad[padnumber].keyright) = 1 OR jx > 0.8
isright = TRUE
ENDIF
IF isfire
INC sfPad[padnumber].sffire
ELSE
sfPad[padnumber].sffire= 0 // sffire = 1 then fire. sffire > 1 then IMMA CHARGIN' MAH LAZER!
ENDIF
IF isjump
INC sfPad[padnumber].sfjump
ELSE
sfPad[padnumber].sfjump = 0 // sfjump = 1 then just pressed jump. sfjump > 1 then still holding it.
ENDIF
IF isup AND isright
sfPad[padnumber].sfdirection = 1
ELSEIF isup AND isleft
sfPad[padnumber].sfdirection = 7
ELSEIF isdown AND isright
sfPad[padnumber].sfdirection = 3
ELSEIF isdown AND isleft
sfPad[padnumber].sfdirection = 5
ELSEIF isup
sfPad[padnumber].sfdirection = 8
ELSEIF isdown
sfPad[padnumber].sfdirection = 4
ELSEIF isright
sfPad[padnumber].sfdirection = 2
ELSEIF isleft
sfPad[padnumber].sfdirection = 6
ELSE
sfPad[padnumber].sfdirection = 9
ENDIF
IF cancelcharge = TRUE THEN sfPad[padnumber].sffire = 0
// Check Special Moves
IF sfPad[padnumber].sfspecial = 0 OR cancelspecials = TRUE // Are there any special moves defined for this joypad? Are we even checking for special moves this cycle?
// do nothing
ELSE
FOR move = 0 TO sfPad[padnumber].sfspecial - 1
SELECT sfPad[padnumber].sftypemove[move]
CASE 1 // the type of move is a step by step move
IF sfPad[padnumber].sfmovestate[move] = 255 THEN sfPad[padnumber].sfmovestate[move] = 0 // if special move was triggered last cycle, remove the flag
state = sfPad[padnumber].sfmovestate[move]
IF state = 0 // here we have the start of lots of IF statements. My favourite! >:(
checkmove = sfPad[padnumber].sfmoves[move][0][0]
checkbutton = sfPad[padnumber].sfmoves[move][0][1]
IF sfPad[padnumber].sfcanignorefire[move] = 1
checkbutton = jumpfire
ENDIF
IF checkmove = sfPad[padnumber].sfdirection AND checkbutton = jumpfire
sfPad[padnumber].sfmovestate[move]=1
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
ENDIF
ELSE
checkmove = sfPad[padnumber].sfmoves[move][state-1][0]
checkbutton = sfPad[padnumber].sfmoves[move][state-1][1]
checknextmove = sfPad[padnumber].sfmoves[move][state][0]
checknextbutton = sfPad[padnumber].sfmoves[move][state][1]
IF sfPad[padnumber].sfcanignorefire[move] = 1
checkbutton = jumpfire
checknextbutton = jumpfire
ENDIF
IF checkmove = sfPad[padnumber].sfdirection AND checkbutton = jumpfire
INC sfPad[padnumber].sfmovecounter[move]
IF sfPad[padnumber].sfmovecounter[move] > sfPad[padnumber].sfmovewait[move]
sfPad[padnumber].sfmovestate[move]=0
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
ENDIF
ELSE
IF checknextmove = sfPad[padnumber].sfdirection AND checknextbutton = jumpfire
INC sfPad[padnumber].sfmovestate[move]
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
ELSE
INC sfPad[padnumber].sfmovetolerance[move]
IF sfPad[padnumber].sfmovetolerance[move] > sfPad[padnumber].sftolerance[move]
sfPad[padnumber].sfmovestate[move]=0
sfPad[padnumber].sfmovecounter[move]=0
sfPad[padnumber].sfmovetolerance[move]=0
ENDIF
ENDIF
ENDIF
ENDIF
IF sfPad[padnumber].sfmovestate[move] = sfPad[padnumber].sfendmove[move] THEN sfPad[padnumber].sfmovestate[move] = 255
CASE 2 // the move is a rapid fire sequence.
state = sfPad[padnumber].sfmovestate[move]
sfPad[padnumber].sfendmove[move]=0
IF state = 0 AND isfire
sfPad[padnumber].sfmovestate[move]=1
sfPad[padnumber].sfmovetolerance[move]=32
sfPad[padnumber].sfmovecounter[move]=0
ENDIF
IF state > 0
IF sfPad[padnumber].sffire > sfPad[padnumber].sftolerance[move]
sfPad[padnumber].sfmovestate[move]=0 // if time fire is held is beyond tolerance, you are too slow!
ELSEIF sfPad[padnumber].sfmovecounter[move] > sfPad[padnumber].sfmovewait[move]
sfPad[padnumber].sfmovestate[move]=0
ENDIF
ENDIF
IF state > 0
IF sfPad[padnumber].sffire = 0 AND sfPad[padnumber].sfmovetolerance[move] = 32
INC sfPad[padnumber].sfmovestate[move]
sfPad[padnumber].sfmovetolerance[move] = 64
ELSEIF sfPad[padnumber].sffire > 0 AND sfPad[padnumber].sfmovetolerance[move] = 64
INC sfPad[padnumber].sfmovestate[move]
sfPad[padnumber].sfmovetolerance[move] = 32
sfPad[padnumber].sfmovecounter[move]=0
ELSEIF sfPad[padnumber].sfmovetolerance[move] = 64
INC sfPad[padnumber].sfmovecounter[move]
ENDIF
ENDIF
IF state > sfPad[padnumber].sfcanignorefire[move]
sfPad[padnumber].sfendmove[move] = 255
ENDIF
// Add hold and release special move routine here
// e.g. Guile's "Sonic Boom" move, where the joystick is held back for two seconds then moved forward and fire pressed.
ENDSELECT
NEXT
ENDIF
ENDFUNCTION