' ################################################################################################################################################################
' #TOP
' Game Input Mapping Test
' Version 0.90 by madscijr
' BASED ON CODE BY SMcNeill FROM:
' Simple Joystick Detection and Interaction (Read 316 times)
' https://www.qb64.org/forum/index.php?topic=2160.msg129051#msg129051
' https://qb64forum.alephc.xyz/index.php?topic=2160.msg129083#msg129083
' and others (sources cited throughout).
' ################################################################################################################################################################
' #CONSTANTS = GLOBAL CONSTANTS
' boolean constants:
Const cMaxControllers
= 8
' Use as index for array of ControlInputType
Const c_iKeyDown_F10
= 17408 Const c_iKeyHit_AltLeft
= -30764 Const c_iKeyHit_AltRight
= -30765
' ################################################################################################################################################################
' #UDT #TYPES = USER DEFINED TYPES
' UDT TO HOLD THE INFO FOR A PLAYER
c
AS INTEGER ' character to display on screen
' UDT TO HOLD THE INFO FOR A GAME CONTROLLER
' UDT TO HOLD THE INFO FOR A GAME CONTROLLER
typ
As Integer ' cInputKey, cInputButton, cInputAxis
'' KEY MAPPING v1:
'Type DirKeyMapType
' KeyBack As Long
' KeyForward As Long
' KeyLeft As Long
' KeyRight As Long
' KeyUp As Long
' KeyDown As Long
'End Type ' DirKeyMapType
'
'Type ColorTextType
' s as string
' fg As _Unsigned Long ' foreground color
' bg As _Unsigned Long ' background color
'End Type ' ColorTextType
'
'Type xyByteType
' x as _byte
' y as _byte
'End Type ' xyByteType
'
'Type xyzByteType
' x as _byte
' y as _byte
' z as _byte
'End Type ' xyzByteType
'
'Type xyIntegerType
' x as integer
' y as integer
'End Type ' xyPointType
'
'Type xyzIntegerType
' x as integer
' y as integer
' z as integer
'End Type ' xyzPointType
' ################################################################################################################################################################
' #VARS = GLOBAL VARIABLES
Dim Shared m_ControlMapFileName$: m_ControlMapFileName$
= Left$(m_ProgramName$
, _InStrRev(m_ProgramName$
, ".")) + "map.txt"
' -----------------------------------------------------------------------------
' VARIABLES FOR GRAPHIC PRINTING ROUTINES
' -----------------------------------------------------------------------------
' -----------------------------------------------------------------------------
'Dim Shared m_iMapMinX As Integer: m_iMapMinX = 0
'Dim Shared m_iMapMaxX As Integer: m_iMapMaxX = 64
'Dim Shared m_iMapMidX As Integer: m_iMapMidX = (m_iMapMaxX - m_iMapMinX) \ 2
'Dim Shared m_iMapMinY As Integer: m_iMapMinY = 0
'Dim Shared m_iMapMaxY As Integer: m_iMapMaxY = 64
'Dim Shared m_iMapMidY As Integer: m_iMapMidY = (m_iMapMaxY - m_iMapMinY) \ 2
'Dim Shared m_iMapMinZ As Integer: m_iMapMinZ = 0
'Dim Shared m_iMapMaxZ As Integer: m_iMapMaxZ = 64
'Dim Shared m_iMapMidZ As Integer: m_iMapMidZ = (m_iMapMaxZ - m_iMapMinZ) \ 2
'
'Dim Shared m_iPlayerMin As Integer: m_iPlayerMin = 1
'Dim Shared m_iPlayerMax As Integer: m_iPlayerMax = 4
'Dim Shared m_iPlayerCount As Integer: m_iPlayerCount = 0
'Dim Shared m_iObjectCount As Integer: m_iObjectCount = 0 ' <- TO BE USED WHEN WE HAVE OBJECTS
'
'Dim Shared m_arrMap(m_iMapMinX To m_iMapMaxX, m_iMapMinY To m_iMapMaxY, m_iMapMinZ To m_iMapMaxZ) As MapTileType
'Dim Shared m_arrRender0(m_iMapMinX To m_iMapMaxX, m_iMapMinY To m_iMapMaxY, m_iMapMinZ To m_iMapMaxZ) As MapTileType
'Dim Shared m_arrRender1(m_iMapMinX To m_iMapMaxX, m_iMapMinY To m_iMapMaxY, m_iMapMinZ To m_iMapMaxZ) As MapTileType
'Dim Shared m_arrRender2(m_iMapMinX To m_iMapMaxX, m_iMapMinY To m_iMapMaxY, m_iMapMinZ To m_iMapMaxZ) As MapTileType
'Dim Shared m_arrPlayer(m_iPlayerMin To m_iPlayerMax) As PlayerType
'' For each player, map the 6 directional keys differently for each of the 6 directional orientations!
'Dim Shared m_arrDirKeyMap(m_iPlayerMin To m_iPlayerMax, c_iDir_Min To c_iDir_Max) As DirKeyMapType
ReDim Shared m_arrPlayer
(1 To 8) As PlayerType
' holds info for each player ReDim Shared m_arrControlMap
(1 To 8, 1 To 8) As ControlInputType
' holds control mapping for each player (player #, direction) ReDim Shared m_arrController
(1 To 8) As ControllerType
' holds info for each game controller
' ENABLE / DISABLE DEBUG CONSOLE
' =============================================================================
' LOCAL VARIABLES
' ****************************************************************************************************************************************************************
' ACTIVATE DEBUGGING WINDOW
_Echo "Started " + m_ProgramName$
' ****************************************************************************************************************************************************************
' =============================================================================
' START THE MAIN ROUTINE
main
' =============================================================================
' FINISH
Print m_ProgramName$
+ " finished." Input "Press <ENTER> to continue", in$
' ****************************************************************************************************************************************************************
' DEACTIVATE DEBUGGING WINDOW
' ****************************************************************************************************************************************************************
System ' return control to the operating system
' /////////////////////////////////////////////////////////////////////////////
Dim result$ : result$
= ""
' SET UP SCREEN
Print "Game Input Mapping Test" Print "v0.90, by Softintheheadware (Jan, 2022)"
Print "1. Basic controller test" Print "2. Load controller mapping" Print "3. View controller mapping" Print "4. Edit controller mapping for 1 or more players" Print " Note: not tested yet" Print "5. Reset controller mapping for 1 or more players" Print "6. Map controllers for 1 or more players" Print "7. Test controller mappings to move around screen" Print " Issue #1: Keyboard input not working yet" Print " Issue #2: Digital joysticks don't allow continuous movement" Print " Issue #3: Need analog joystick sensitivity adjustment option" Print "8. Save controller mappings" Print "What to do? ('q' to exit)"
result$ = TestJoysticks$
result$ = LoadMappings$
if len(result$
)=0 then result$
="Loaded mappings." result$ = ViewMappings$
result$ = EditMappings$
result$ = ResetMapping$
result$ = MapInput$
result$ = TestMappings$
result$ = SaveMappings$
' RETURN TO TEXT SCREEN
' /////////////////////////////////////////////////////////////////////////////
' TODO: get keyboard input working
' TODO: get continuous movement working for digital joysticks
' TODO: adjust analog joystick sensitivity
DIM arrButton
(32, 16) As Integer ' number of buttons on the joystick DIM arrButtonNew
(32, 16) As Integer ' tracks when to initialize values DIM arrAxis
(32, 16) As Double ' number of axis on the joystick DIM arrAxisNew
(32, 16) As Integer ' tracks when to initialize values
' INITIALIZE
iMinX = 0 : iMaxX = iCols
iMinY = 0 : iMaxY = iRows
' INITIALIZE PLAYER COORDINATES AND SCREEN CHARACTERS
iNextY = 1
iNextX = -3
iNextC = 64
iNextX = iNextX + 4
iNextX = iMinX
iNextY = iNextY + 4
iNextY = iMinY
iNextC = iNextC + 1
m_arrPlayer(iPlayer).x = iNextX
m_arrPlayer(iPlayer).y = iNextY
m_arrPlayer(iPlayer).c = iNextC
m_arrPlayer(iPlayer).xOld = iNextX
m_arrPlayer(iPlayer).yOld = iNextY
m_arrPlayer(iPlayer).moveX = 0
m_arrPlayer(iPlayer).lastMoveX = 0
m_arrPlayer(iPlayer).moveY = 0
m_arrPlayer(iPlayer).lastMoveY = 0
m_arrPlayer(iPlayer).button1 = 0
m_arrPlayer(iPlayer).lastButton1 = 0
m_arrPlayer(iPlayer).button2 = 0
m_arrPlayer(iPlayer).lastButton2 = 0
m_arrPlayer(iPlayer).button3 = 0
m_arrPlayer(iPlayer).lastButton3 = 0
m_arrPlayer(iPlayer).button4 = 0
m_arrPlayer(iPlayer).lastButton4 = 0
' COUNT # OF JOYSTICKS
' TODO: find out the right way to count joysticks
' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
iDeviceCount
= _DEVICES ' Find the number of devices on someone's system
' LIMIT # OF DEVICES, IF THERE IS A LIMIT DEFINED
iNumControllers = iDeviceCount - 2
IF iNumControllers
> cMaxControllers
THEN iNumControllers = cMaxControllers
' ONLY 2 FOUND (KEYBOARD, MOUSE)
'sError = "No game controllers found."
iNumControllers = 0
' INITIALIZE CONTROLLER DATA
FOR iController
= 1 TO iNumControllers
m_arrController(iController).buttonCount = cMaxButtons
m_arrController(iController).axisCount = cMaxAxis
for iLoop
= 1 to cMaxButtons
arrButtonNew(iController, iLoop) = TRUE
for iLoop
= 1 to cMaxAxis
arrAxisNew(iController, iLoop) = TRUE
' INITIALIZE CONTROLLER INPUT
FOR iController
= 1 TO iNumControllers
iDevice = iController + 2
m_arrController(iController).buttonCount = iLoop
arrButton(iController, iLoop) = FALSE
m_arrController(iController).axisCount = iLoop
arrAxis(iController, iLoop) = 0
WEND ' clear and update the device buffer
' GET INPUT AND MOVE PLAYERS AROUND ON SCREEN
bFinished = FALSE
' Clear control buffer for players
m_arrPlayer(iPlayer).lastMoveX = m_arrPlayer(iPlayer).moveX
m_arrPlayer(iPlayer).moveX = 0
m_arrPlayer(iPlayer).lastMoveY = m_arrPlayer(iPlayer).moveY
m_arrPlayer(iPlayer).moveY = 0
m_arrPlayer(iPlayer).lastButton1 = m_arrPlayer(iPlayer).button1
m_arrPlayer(iPlayer).button1 = 0
m_arrPlayer(iPlayer).lastButton2 = m_arrPlayer(iPlayer).button2
m_arrPlayer(iPlayer).button2 = 0
m_arrPlayer(iPlayer).lastButton3 = m_arrPlayer(iPlayer).button3
m_arrPlayer(iPlayer).button3 = 0
m_arrPlayer(iPlayer).lastButton4 = m_arrPlayer(iPlayer).button4
m_arrPlayer(iPlayer).button4 = 0
' -----------------------------------------------------------------------------
' BEGIN CHECK FOR CONTROLLER INPUT
FOR iController
= 1 TO iNumControllers
iDevice = iController + 2
' Check all devices
' Check each button
' update button array to indicate if a button is up or down currently.
'IF _BUTTONCHANGE(iLoop) THEN
if iValue
<> arrButton
(iController
, iLoop
) then ' *****************************************************************************
' PRESSED BUTTON
' BEGIN find who this is mapped for
bFoundWho = FALSE
if m_arrControlMap
(iPlayer
, iWhichInput
).device
= iDevice
then if m_arrControlMap
(iPlayer
, iWhichInput
).typ
= cInputButton
then if m_arrControlMap
(iPlayer
, iWhichInput
).code
= iLoop
then 'if m_arrControlMap(iPlayer, iWhichInput).value = iValue then
bFoundWho = TRUE
m_arrPlayer(iPlayer).moveY = -1
m_arrPlayer(iPlayer).moveY = 1
m_arrPlayer(iPlayer).moveX = -1
m_arrPlayer(iPlayer).moveX = 1
m_arrPlayer(iPlayer).button1 = TRUE
m_arrPlayer(iPlayer).button2 = TRUE
m_arrPlayer(iPlayer).button3 = TRUE
m_arrPlayer(iPlayer).button4 = TRUE
'(IGNORE)
'end if
' END find who this is mapped for
' Check each axis
dblNextAxis
= _AXIS(iLoop
) dblNextAxis = RoundUpDouble# (dblNextAxis, 3)
' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
' which is prone to always give minute values and never really center on true 0.
' A value of 1 means my axis is pushed fully in one direction.
' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
' Set sensitivity:
'These are way too sensitive for analog:
'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .001 THEN
''For digital input, we'll use a big picture:
'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= 0.75 THEN
' WE WANT CONTINUOUS MOVEMENT (DISABLE FOR NOT)
'if dblNextAxis <> arrAxis(iController, iLoop) then
' *****************************************************************************
' MOVED STICK
' convert to a digital value
iValue = -1
iValue = 1
' BEGIN find who this is mapped for
bFoundWho = FALSE
if m_arrControlMap
(iPlayer
, iWhichInput
).device
= iDevice
then if m_arrControlMap
(iPlayer
, iWhichInput
).typ
= cInputAxis
then if m_arrControlMap
(iPlayer
, iWhichInput
).code
= iLoop
then if m_arrControlMap
(iPlayer
, iWhichInput
).value
= iValue
then bFoundWho = TRUE
m_arrPlayer(iPlayer).moveY = -1
m_arrPlayer(iPlayer).moveY = 1
m_arrPlayer(iPlayer).moveX = -1
m_arrPlayer(iPlayer).moveX = 1
m_arrPlayer(iPlayer).button1 = TRUE
m_arrPlayer(iPlayer).button2 = TRUE
m_arrPlayer(iPlayer).button3 = TRUE
m_arrPlayer(iPlayer).button4 = TRUE
'(IGNORE)
' END find who this is mapped for
WEND ' clear and update the device buffer
' END CHECK FOR CONTROLLER INPUT
' -----------------------------------------------------------------------------
' -----------------------------------------------------------------------------
' BEGIN CHECK FOR KEYBOARD INPUT #1
'_KEYCLEAR: _DELAY 1
' Detect changed key state
iCode = m_arrButtonCode(iLoop)
' *****************************************************************************
' PRESSED KEYBOARD
'PRINT "PRESSED " + m_arrButtonKey(iLoop)
' BEGIN find who this is mapped for
bFoundWho = FALSE
if m_arrControlMap
(iPlayer
, iWhichInput
).device
= iDevice
then if m_arrControlMap
(iPlayer
, iWhichInput
).typ
= cInputKey
then
'if m_arrControlMap(iPlayer, iWhichInput).code = iLoop then
if m_arrControlMap
(iPlayer
, iWhichInput
).code
= iCode
then 'if m_arrControlMap(iPlayer, iWhichInput).value = iValue then
bFoundWho = TRUE
m_arrPlayer(iPlayer).moveY = -1
m_arrPlayer(iPlayer).moveY = 1
m_arrPlayer(iPlayer).moveX = -1
m_arrPlayer(iPlayer).moveX = 1
m_arrPlayer(iPlayer).button1 = TRUE
m_arrPlayer(iPlayer).button2 = TRUE
m_arrPlayer(iPlayer).button3 = TRUE
m_arrPlayer(iPlayer).button4 = TRUE
'(IGNORE)
'end if
' END find who this is mapped for
' END CHECK FOR KEYBOARD INPUT #1
' -----------------------------------------------------------------------------
' NOW DRAW PLAYERS ON SCREEN
' MOVE RIGHT/LEFT
m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).x + m_arrPlayer(iPlayer).moveX
if m_arrPlayer
(iPlayer
).x
< iMinX
then m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).xOld ' iMinX
m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).xOld ' iMaxX
' MOVE UP/DOWN
m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).y + m_arrPlayer(iPlayer).moveY
if m_arrPlayer
(iPlayer
).y
< iMinY
then m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).yOld ' iMinY
m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).yOld ' iMaxY
' UPDATE SCREEN
'_PRINTSTRING (m_arrPlayer(iPlayer).xOld, m_arrPlayer(iPlayer).yOld), " "
'_PRINTSTRING (m_arrPlayer(iPlayer).x, m_arrPlayer(iPlayer).y), CHR$(m_arrPlayer(iPlayer).c)
PrintString m_arrPlayer(iPlayer).xOld, m_arrPlayer(iPlayer).yOld, " "
PrintString m_arrPlayer
(iPlayer
).x
, m_arrPlayer
(iPlayer
).y
, CHR$(m_arrPlayer
(iPlayer
).c
) m_arrPlayer(iPlayer).xOld = m_arrPlayer(iPlayer).x
m_arrPlayer(iPlayer).yOld = m_arrPlayer(iPlayer).y
' BUTTON ACTIONS
if m_arrPlayer
(iPlayer
).button1
= TRUE
then MakeSound iPlayer, 1
if m_arrPlayer
(iPlayer
).button2
= TRUE
then MakeSound iPlayer, 2
if m_arrPlayer
(iPlayer
).button3
= TRUE
then MakeSound iPlayer, 3
if m_arrPlayer
(iPlayer
).button4
= TRUE
then MakeSound iPlayer, 4
' /////////////////////////////////////////////////////////////////////////////
iPlayer = 1
iPlayer = 8
iButton = 1
iButton = 4
note% = iPlayer * 100 + (iButton * 25)
note% = 4186
' /////////////////////////////////////////////////////////////////////////////
' V2 prints in 2 columns.
Dim RoutineName
As String:: RoutineName
= "PrintControllerMap2"
' INITIALIZE
InitKeyboardButtonCodes
' START OUTPUT
Print "Controller mapping:" 'Print "Player# Input Device# Type Code Value"
' 1 button #2 x unknown x x
' 9 11 9 9 18 9
' 12345678912345678901123456789123456789123456789012345678123456789
' 12345678901234567890123456789012345678901234567890123456789012345678901234567890
' 00000000011111111112222222222333333333344444444445555555555666666666677777777778
' THIS IS A LAZY WAY TO GET 2 COLUMNS!
iHalf
= UBound(m_arrControlMap
, 1) / 2
sLine = "Player# Input Device# Type Code Value"
sColumn1
= sColumn1
+ StrPadRight$
(sLine
, iColWidth
) + chr$(13) sLine = "-------------------------------------------------------------"
sColumn1
= sColumn1
+ StrPadRight$
(sLine
, iColWidth
) + chr$(13) iCount = 0
if InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) <> "unknown" then iCount = iCount + 1
if InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) <> "unknown" then sLine = IntPadRight$(iPlayer, 9)
sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
'sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
if m_arrControlMap
(iPlayer
, iWhichInput
).typ
= cInputKey
then sValue = GetKeyboardButtonCodeText$( m_arrControlMap(iPlayer, iWhichInput).code )
sValue = StrPadRight$(sValue, 18)
sValue = IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 18)
sLine = sLine + sValue
sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
'Print sLine
sLine = StrPadRight$(sLine, iColWidth)
sColumn1
= sColumn1
+ sLine
+ chr$(13) sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
'Print sLine
sLine = StrPadRight$(sLine, iColWidth)
sColumn1
= sColumn1
+ sLine
+ chr$(13)
sLine = "Player# Input Device# Type Code Value"
sColumn2
= sColumn2
+ StrPadRight$
(sLine
, iColWidth
) + chr$(13) sLine = "-------------------------------------------------------------"
sColumn2
= sColumn2
+ StrPadRight$
(sLine
, iColWidth
) + chr$(13) iCount = 0
if InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) <> "unknown" then iCount = iCount + 1
if InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) <> "unknown" then sLine = IntPadRight$(iPlayer, 9)
sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
'sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
if m_arrControlMap
(iPlayer
, iWhichInput
).typ
= cInputKey
then sValue = GetKeyboardButtonCodeText$( m_arrControlMap(iPlayer, iWhichInput).code )
sValue = StrPadRight$(sValue, 18)
sValue = IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 18)
sLine = sLine + sValue
sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
'Print sLine
sLine = StrPadRight$(sLine, iColWidth)
sColumn2
= sColumn2
+ sLine
+ chr$(13) sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
'Print sLine
sLine = StrPadRight$(sLine, iColWidth)
sColumn2
= sColumn2
+ sLine
+ chr$(13)
split sColumn1
, chr$(13), arrColumn1
() split sColumn2
, chr$(13), arrColumn2
() sLine = ""
sLine = sLine + arrColumn1(iLoop)
sLine
= sLine
+ STRING$(iColWidth
, " ") sLine = sLine + " "
sLine = sLine + arrColumn2(iLoop)
sLine
= sLine
+ STRING$(iColWidth
, " ")
' /////////////////////////////////////////////////////////////////////////////
' Original (simple) routine
Dim RoutineName
As String:: RoutineName
= "PrintControllerMap1"
' INITIALIZE
InitKeyboardButtonCodes
' OUTPUT MAPPING
Print "Controller mapping:" Print "Player# Input Device# Type Code Value" ' 1 button #2 x unknown x x
' 9 11 9 9 9 9
' 12345678912345678901123456789123456789123456789123456789
' 12345678901234567890123456789012345678901234567890123456789012345678901234567890
iCount = 0
if InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) <> "unknown" then iCount = iCount + 1
if InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) <> "unknown" then sLine = IntPadRight$(iPlayer, 9)
sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
' /////////////////////////////////////////////////////////////////////////////
' INITIALIZE
InitKeyboardButtonCodes
' Try loading map
sResult = LoadControllerMap$
LoadMappings$ = sResult
' /////////////////////////////////////////////////////////////////////////////
' INITIALIZE
InitKeyboardButtonCodes
' Try saving map
sResult = SaveControllerMap$
SaveMappings$ = sResult
' /////////////////////////////////////////////////////////////////////////////
' INITIALIZE
InitKeyboardButtonCodes
PrintControllerMap2
Input "PRESS <ENTER> TO CONTINUE", in$
ViewMappings$ = ""
' /////////////////////////////////////////////////////////////////////////////
' TODO: test this
Dim RoutineName
As String: RoutineName
= "EditMappings$"
' INITIALIZE
InitKeyboardButtonCodes
' EDIT
PrintControllerMap2
Print "To edit a mapping, enter a player number: " _
"1-" + cstr$(cMaxPlayers) + ", " + _
cstr$(cMaxPlayers+1) + ") or 0 to exit."
Input "Edit mapping for player? ";iPlayer
if iPlayer
> 0 and iPlayer
<= cMaxPlayers
then bContinue2 = TRUE
Print "Editing mappings for player " + cstr$
(iPlayer
) + "." Print right$(" " + cstr$
(iWhichInput
), 2) + ". " + InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) Input "Type # of control to edit or q to quit editing player.";in$
bContinue3 = TRUE
Print "Settings for " + chr$(34) + InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) + chr$(34) + ":" Print "1. Device # : " + cstr$
(m_arrControlMap
(iPlayer
, iWhichInput
).device
) Print "2. Device type: " + InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) Print "3. Input code : " + _Trim$(Str$(m_arrControlMap
(iPlayer
, iWhichInput
).code
)) Print "4. Input value: " + _Trim$(Str$(m_arrControlMap
(iPlayer
, iWhichInput
).value
)) INPUT "Change item? (1-4 or q to quit editing control)";in$
Print "Change the device number." Input "Type a new device #, 0 for none, or blank to leave it unchanged";in$
m_arrControlMap(iPlayer, iWhichInput).device = iDevice
Print "Updated device number. Remember to save mappings when done." bContinue4 = TRUE
Print "Change the device type." Print cstr$
(cInputKey
) + "=keyboard" Print cstr$
(cInputButton
) + "=game controller button" Print cstr$
(cInputAxis
) + "=game controller joystick/axis" Print cstr$
(cInputNone
) + "=none" Input "Device type or blank to leave it unchanged";in$
if iType
=cInputKey
or iType
=cInputButton
or _
iType
=cInputAxis
or iType
=cInputNone
then
m_arrControlMap(iPlayer, iWhichInput).typ = iType
Print "Updated device type. Remember to save mappings when done." Print "Please choose one of the listed values." Print "Change the input code." Input "Type a new input code, or blank to leave it unchanged";in$
m_arrControlMap(iPlayer, iWhichInput).code = iWhich
Print "Updated input code. Remember to save mappings when done." Print "Change the input value." Input "Type a new input value, or blank to leave it unchanged";in$
m_arrControlMap(iPlayer, iWhichInput).value = value
Print "Updated input value. Remember to save mappings when done." Print "Please choose a number between 1 and 4." Print "Please choose a number between " + cstr$
(lbound(m_arrControlMap
, 2)) + " and " + cstr$
(ubound(m_arrControlMap
, 2)) + "." if len(sResult
) = 0 then sResult
= "(Cancelled.)"
EditMappings$ = sResult
' /////////////////////////////////////////////////////////////////////////////
Dim RoutineName
As String: RoutineName
= "ResetMapping$"
' INITIALIZE
InitKeyboardButtonCodes
' RESET
PrintControllerMap2
Print "To delete mapping, enter a player number: " _
"1-" + cstr$(cMaxPlayers) + ", " + _
cstr$(cMaxPlayers+1) + " for all, or 0 to exit."
Input "Delete mapping for player? ";iPlayer
if iPlayer
> 0 and iPlayer
<= cMaxPlayers
then Print "Delete mappings for player " + cstr$
(iPlayer
) + "." if InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) <> "unknown" then m_arrControlMap(iPlayer, iWhichInput).device = 0
m_arrControlMap(iPlayer, iWhichInput).typ = 0
m_arrControlMap(iPlayer, iWhichInput).code = 0
m_arrControlMap(iPlayer, iWhichInput).value = 0
sResult = "Mappings deleted for player " + cstr$(iPlayer) + "."
For iPlayer
= 1 to cMaxPlayers
if InputTypeToString$
(m_arrControlMap
(iPlayer
, iWhichInput
).typ
) <> "unknown" then m_arrControlMap(iPlayer, iWhichInput).device = 0
m_arrControlMap(iPlayer, iWhichInput).typ = 0
m_arrControlMap(iPlayer, iWhichInput).code = 0
m_arrControlMap(iPlayer, iWhichInput).value = 0
sResult = "All mappings deleted."
if len(sResult
) = 0 then sResult
= "(Cancelled.)" ResetMapping$ = sResult
' /////////////////////////////////////////////////////////////////////////////
' INITIALIZE
InitKeyboardButtonCodes
' SET UP SCREEN
' MAKE SURE WE HAVE DEVICES
' 1 is the keyboard
' 2 is the mouse
' 3 is the joystick
' unless someone has a strange setup with multiple mice/keyboards/ect...
' In that case, you can use _DEVICE$(i) to look for "KEYBOARD", "MOUSE", "JOYSTICK", if necessary.
' I've never actually found it necessary, but I figure it's worth mentioning, just in case...
iDeviceCount
= _DEVICES ' Find the number of devices on someone's system '' Try loading map
'sError = LoadControllerMap$
'if len(sError) = 0 then
' print "Previous controller mapping loaded."
'else
' print "*******************************************************************************"
' print "There were errors loading the controller mapping file:"
' print sError
' print
' print "Try remapping - a new file will be created."
' print "*******************************************************************************"
'end if
PrintControllerMap2
Print "To edit mapping, enter a player number (1-" + cstr$
(cMaxPlayers
) + ") or 0 to exit." Input "Get input for player? ";iPlayer
if iPlayer
> 0 and iPlayer
<= cMaxPlayers
then sResult = MapInput1$(iPlayer)
Print "Remember to save mappings when done." sResult = "(Cancelled.)"
sResult = "No controller devices found."
INPUT "PRESS <ENTER> TO CONTINUE", in$
MapInput$ = sResult
' /////////////////////////////////////////////////////////////////////////////
' Detect controls
' THIS VERSION SUPPORTS UPTO 8 JOYSTICKS, WITH UPTO 2 BUTTONS AND 2 AXES EACH
' (THIS IS FOR ATARI 2600 JOYSTICKS)
' The following shared arrays must be declared:
' ReDim Shared m_arrButtonCode(1 To 99) As Long
' ReDim Shared m_arrButtonKey(1 To 99) As String
DIM arrButton
(32, 16) As Integer ' number of buttons on the joystick DIM arrButtonNew
(32, 16) As Integer ' tracks when to initialize values DIM arrAxis
(32, 16) As Double ' number of axis on the joystick DIM arrAxisNew
(32, 16) As Integer ' tracks when to initialize values
'Dim arrInput(1 To 8) As ControlInputType
' FOR PRINTING OUTPUT
DIM iDigits
AS INTEGER ' # digits to display (values are truncated to this length)
' INITIALIZE
iDigits = 4 ' 11
iColCount = 3
iGroupCount = 0 ' re-initialized at the top of every loop
' COUNT # OF JOYSTICKS
' TODO: find out the right way to count joysticks
' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
iDeviceCount
= _DEVICES ' Find the number of devices on someone's system
' LIMIT # OF DEVICES, IF THERE IS A LIMIT DEFINED
iNumControllers = iDeviceCount - 2
IF iNumControllers
> cMaxControllers
THEN iNumControllers = cMaxControllers
' ONLY 2 FOUND (KEYBOARD, MOUSE)
sError = "No game controllers found."
' INITIALIZE CONTROLLER DATA
FOR iController
= 1 TO iNumControllers
m_arrController(iController).buttonCount = cMaxButtons
m_arrController(iController).axisCount = cMaxAxis
for iLoop
= 1 to cMaxButtons
arrButtonNew(iController, iLoop) = TRUE
for iLoop
= 1 to cMaxAxis
arrAxisNew(iController, iLoop) = TRUE
' INITIALIZE CONTROLLER INPUT
Print "We will now detect controllers." Print "Do not touch any keys or game controllers during detection." Input "Press <ENTER> to begin";in$
sLine
= "Initializing controllers" :
Print sLine;
iMaxCols
= (iCols
- len(sLine
)) - 1 iCount = 0
iCount = iCount + 1
FOR iController
= 1 TO iNumControllers
iDevice = iController + 2
m_arrController(iController).buttonCount = iLoop
'IF _BUTTONCHANGE(iLoop) THEN
' arrButton(iController, iLoop) = _BUTTON(iLoop)
'END IF
arrButton(iController, iLoop) = FALSE
m_arrController(iController).axisCount = iLoop
arrAxis(iController, iLoop) = 0
WEND ' clear and update the device buffer LOOP UNTIL iCount
> 60 ' quit after 2 seconds
' WAIT FOR INPUT
Print "Press <ESCAPE> to cancel at any time."
bCancel = FALSE
bFinished = FALSE
iLastPressed = 0
'print "iWhichInput=" + cstr$(iWhichInput)
Print "Player #" + cstr$
(iPlayer
) + " press control for " + InputToString$
(iWhichInput
) + " or ESC to skip: ";
' =============================================================================
' BEGIN LOOK FOR NEXT INPUT
bMoveNext = FALSE
' -----------------------------------------------------------------------------
' BEGIN CHECK FOR CONTROLLER INPUT
FOR iController
= 1 TO iNumControllers
iDevice = iController + 2
' Check all devices
' Check each button
'm_arrController(iController).buttonCount = iLoop
' update button array to indicate if a button is up or down currently.
if iValue
<> arrButton
(iController
, iLoop
) then ' *****************************************************************************
' PRESSED BUTTON
' make sure this isn't already mapped
bHaveInput = TRUE
' is input unique?
For iNextInput
= lbound(m_arrControlMap
, 2) to iWhichInput
-1 if m_arrControlMap
(iPlayer
, iNextInput
).device
= iDevice
then if m_arrControlMap
(iPlayer
, iNextInput
).typ
= cInputButton
then if m_arrControlMap
(iPlayer
, iNextInput
).code
= iLoop
then if m_arrControlMap
(iPlayer
, iNextInput
).value
= iValue
then bHaveInput = FALSE
m_arrControlMap(iPlayer, iWhichInput).device = iDevice
m_arrControlMap(iPlayer, iWhichInput).typ = cInputButton
m_arrControlMap(iPlayer, iWhichInput).code = iLoop
m_arrControlMap(iPlayer, iWhichInput).value = iValue
bMoveNext = TRUE
' Check each axis
'm_arrController(iController).axisCount = iLoop
dblNextAxis
= _AXIS(iLoop
) dblNextAxis = RoundUpDouble# (dblNextAxis, 3)
' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
' which is prone to always give minute values and never really center on true 0.
' A value of 1 means my axis is pushed fully in one direction.
' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
'These are way too sensitive for analog:
'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .001 THEN
'For digital input, we'll use a big picture:
if dblNextAxis
<> arrAxis
(iController
, iLoop
) then ' *****************************************************************************
' MOVED STICK
' convert to a digital value
iValue = -1
iValue = 1
' make sure this isn't already mapped
bHaveInput = TRUE
' is input unique?
For iNextInput
= lbound(m_arrControlMap
, 2) to iWhichInput
-1 if m_arrControlMap
(iPlayer
, iNextInput
).device
= iDevice
then if m_arrControlMap
(iPlayer
, iNextInput
).typ
= cInputAxis
then if m_arrControlMap
(iPlayer
, iNextInput
).code
= iLoop
then if m_arrControlMap
(iPlayer
, iNextInput
).value
= iValue
then bHaveInput = FALSE
m_arrControlMap(iPlayer, iWhichInput).device = iDevice
m_arrControlMap(iPlayer, iWhichInput).typ = cInputAxis
m_arrControlMap(iPlayer, iWhichInput).code = iLoop
m_arrControlMap(iPlayer, iWhichInput).value = iValue
bMoveNext = TRUE
WEND ' clear and update the device buffer
' END CHECK FOR CONTROLLER INPUT
' -----------------------------------------------------------------------------
' -----------------------------------------------------------------------------
' BEGIN CHECK FOR KEYBOARD INPUT #1
'_KEYCLEAR: _DELAY 1
' Detect changed key state
iCode = m_arrButtonCode(iLoop)
' *****************************************************************************
' PRESSED KEYBOARD
'PRINT "PRESSED " + m_arrButtonKey(iLoop)
' make sure this isn't already mapped
bHaveInput = TRUE
' is input unique?
For iNextInput
= lbound(m_arrControlMap
, 2) to iWhichInput
-1 if m_arrControlMap
(iPlayer
, iNextInput
).device
= 1 then ' .device 1 = keyboard if m_arrControlMap
(iPlayer
, iNextInput
).typ
= cInputKey
then if m_arrControlMap
(iPlayer
, iNextInput
).code
= iCode
then 'if m_arrControlMap(iPlayer, iNextInput).value = TRUE then
bHaveInput = FALSE
'end if
m_arrControlMap(iPlayer, iWhichInput).device = 1 ' .device 1 = keyboard
m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey
m_arrControlMap(iPlayer, iWhichInput).code = iCode
m_arrControlMap(iPlayer, iWhichInput).value = TRUE
bMoveNext = TRUE
' END CHECK FOR KEYBOARD INPUT #1
' -----------------------------------------------------------------------------
' END LOOK FOR NEXT INPUT
' =============================================================================
Print "Device #" + cstr$
(m_arrControlMap
(iPlayer
, iWhichInput
).device
) + " " + _
InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) + " " + _
cstr$(m_arrControlMap(iPlayer, iWhichInput).code) + " = " + _
cstr$(m_arrControlMap(iPlayer, iWhichInput).value)
bCancel = TRUE
bFinished = TRUE
sResult = "ERRORS: " + sError
MapInput1$ = sResult
' /////////////////////////////////////////////////////////////////////////////
InputToString$ = "up"
InputToString$ = "down"
InputToString$ = "left"
InputToString$ = "right"
InputToString$ = "button #1"
InputToString$ = "button #2"
InputToString$ = "button #3"
InputToString$ = "button #4"
InputToString$ = "unknown"
' /////////////////////////////////////////////////////////////////////////////
InputTypeToString$ = "none"
InputTypeToString$ = "key"
InputTypeToString$ = "button"
InputTypeToString$ = "axis"
InputTypeToString$ = "unknown"
' /////////////////////////////////////////////////////////////////////////////
' METHOD v2 = faster
sResult = m_arrButtonKeyDesc(iCode)
GetKeyboardButtonCodeText$ = sResult
' /////////////////////////////////////////////////////////////////////////////
' METHOD v2
' Faster lookup - a dictionary with a hash lookup would be best
' but this is a quick way to do it since the values never change.
' The following shared arrays must be declared:
' ReDim Shared m_arrButtonCode(1 To 99) As Long
' ReDim Shared m_arrButtonKey(1 To 99) As String
' ReDim Shared m_arrButtonKeyDesc(0 To 512) As String
Sub InitKeyboardButtonCodes
()
' CODE(S) DETECTED WITH _BUTTON:
m_arrButtonCode(1) = 2 : m_arrButtonKey(1) = "Esc"
m_arrButtonCode(2) = 60 : m_arrButtonKey(2) = "F1"
m_arrButtonCode(3) = 61 : m_arrButtonKey(3) = "F2"
m_arrButtonCode(4) = 62 : m_arrButtonKey(4) = "F3"
m_arrButtonCode(5) = 63 : m_arrButtonKey(5) = "F4"
m_arrButtonCode(6) = 64 : m_arrButtonKey(6) = "F5"
m_arrButtonCode(7) = 65 : m_arrButtonKey(7) = "F6"
m_arrButtonCode(8) = 66 : m_arrButtonKey(8) = "F7"
m_arrButtonCode(9) = 67 : m_arrButtonKey(9) = "F8"
m_arrButtonCode(10) = 68 : m_arrButtonKey(10) = "F9"
m_arrButtonCode(11) = 88 : m_arrButtonKey(11) = "F11"
m_arrButtonCode(12) = 89 : m_arrButtonKey(12) = "F12"
m_arrButtonCode(13) = 42 : m_arrButtonKey(13) = "Tilde"
m_arrButtonCode(14) = 3 : m_arrButtonKey(14) = "1"
m_arrButtonCode(15) = 4 : m_arrButtonKey(15) = "2"
m_arrButtonCode(16) = 5 : m_arrButtonKey(16) = "3"
m_arrButtonCode(17) = 6 : m_arrButtonKey(17) = "4"
m_arrButtonCode(18) = 7 : m_arrButtonKey(18) = "5"
m_arrButtonCode(19) = 8 : m_arrButtonKey(19) = "6"
m_arrButtonCode(20) = 9 : m_arrButtonKey(20) = "7"
m_arrButtonCode(21) = 10 : m_arrButtonKey(21) = "8"
m_arrButtonCode(22) = 11 : m_arrButtonKey(22) = "9"
m_arrButtonCode(23) = 12 : m_arrButtonKey(23) = "0"
m_arrButtonCode(24) = 13 : m_arrButtonKey(24) = "Minus"
m_arrButtonCode(25) = 14 : m_arrButtonKey(25) = "Equal"
m_arrButtonCode(26) = 15 : m_arrButtonKey(26) = "BkSp"
m_arrButtonCode(27) = 16 : m_arrButtonKey(27) = "Tab"
m_arrButtonCode(28) = 17 : m_arrButtonKey(28) = "Q"
m_arrButtonCode(29) = 18 : m_arrButtonKey(29) = "W"
m_arrButtonCode(30) = 19 : m_arrButtonKey(30) = "E"
m_arrButtonCode(31) = 20 : m_arrButtonKey(31) = "R"
m_arrButtonCode(32) = 21 : m_arrButtonKey(32) = "T"
m_arrButtonCode(33) = 22 : m_arrButtonKey(33) = "Y"
m_arrButtonCode(34) = 23 : m_arrButtonKey(34) = "U"
m_arrButtonCode(35) = 24 : m_arrButtonKey(35) = "I"
m_arrButtonCode(36) = 25 : m_arrButtonKey(36) = "O"
m_arrButtonCode(37) = 26 : m_arrButtonKey(37) = "P"
m_arrButtonCode(38) = 27 : m_arrButtonKey(38) = "BracketLeft"
m_arrButtonCode(39) = 28 : m_arrButtonKey(39) = "BracketRight"
m_arrButtonCode(40) = 44 : m_arrButtonKey(40) = "Backslash"
m_arrButtonCode(41) = 59 : m_arrButtonKey(41) = "CapsLock"
m_arrButtonCode(42) = 31 : m_arrButtonKey(42) = "A"
m_arrButtonCode(43) = 32 : m_arrButtonKey(43) = "S"
m_arrButtonCode(44) = 33 : m_arrButtonKey(44) = "D"
m_arrButtonCode(45) = 34 : m_arrButtonKey(45) = "F"
m_arrButtonCode(46) = 35 : m_arrButtonKey(46) = "G"
m_arrButtonCode(47) = 36 : m_arrButtonKey(47) = "H"
m_arrButtonCode(48) = 37 : m_arrButtonKey(48) = "J"
m_arrButtonCode(49) = 38 : m_arrButtonKey(49) = "K"
m_arrButtonCode(50) = 39 : m_arrButtonKey(50) = "L"
m_arrButtonCode(51) = 40 : m_arrButtonKey(51) = "Semicolon"
m_arrButtonCode(52) = 41 : m_arrButtonKey(52) = "Apostrophe"
m_arrButtonCode(53) = 29 : m_arrButtonKey(53) = "Enter"
m_arrButtonCode(54) = 43 : m_arrButtonKey(54) = "ShiftLeft"
m_arrButtonCode(55) = 45 : m_arrButtonKey(55) = "Z"
m_arrButtonCode(56) = 46 : m_arrButtonKey(56) = "X"
m_arrButtonCode(57) = 47 : m_arrButtonKey(57) = "C"
m_arrButtonCode(58) = 48 : m_arrButtonKey(58) = "V"
m_arrButtonCode(59) = 49 : m_arrButtonKey(59) = "B"
m_arrButtonCode(60) = 50 : m_arrButtonKey(60) = "N"
m_arrButtonCode(61) = 51 : m_arrButtonKey(61) = "M"
m_arrButtonCode(62) = 52 : m_arrButtonKey(62) = "Comma"
m_arrButtonCode(63) = 53 : m_arrButtonKey(63) = "Period"
m_arrButtonCode(64) = 54 : m_arrButtonKey(64) = "Slash"
m_arrButtonCode(65) = 55 : m_arrButtonKey(65) = "ShiftRight"
m_arrButtonCode(66) = 30 : m_arrButtonKey(66) = "CtrlLeft"
m_arrButtonCode(67) = 348 : m_arrButtonKey(67) = "WinLeft"
m_arrButtonCode(68) = 58 : m_arrButtonKey(68) = "Spacebar"
m_arrButtonCode(69) = 349 : m_arrButtonKey(69) = "WinRight"
m_arrButtonCode(70) = 350 : m_arrButtonKey(70) = "Menu"
m_arrButtonCode(71) = 286 : m_arrButtonKey(71) = "CtrlRight"
m_arrButtonCode(72) = 339 : m_arrButtonKey(72) = "Ins"
m_arrButtonCode(73) = 328 : m_arrButtonKey(73) = "Home"
m_arrButtonCode(74) = 330 : m_arrButtonKey(74) = "PgUp"
m_arrButtonCode(75) = 340 : m_arrButtonKey(75) = "Del"
m_arrButtonCode(76) = 336 : m_arrButtonKey(76) = "End"
m_arrButtonCode(77) = 338 : m_arrButtonKey(77) = "PgDn"
m_arrButtonCode(78) = 329 : m_arrButtonKey(78) = "Up"
m_arrButtonCode(79) = 332 : m_arrButtonKey(79) = "Left"
m_arrButtonCode(80) = 337 : m_arrButtonKey(80) = "Down"
m_arrButtonCode(81) = 334 : m_arrButtonKey(81) = "Right"
m_arrButtonCode(82) = 71 : m_arrButtonKey(82) = "ScrollLock"
m_arrButtonCode(83) = 326 : m_arrButtonKey(83) = "NumLock"
m_arrButtonCode(84) = 310 : m_arrButtonKey(84) = "KeypadSlash"
m_arrButtonCode(85) = 56 : m_arrButtonKey(85) = "KeypadMultiply"
m_arrButtonCode(86) = 75 : m_arrButtonKey(86) = "KeypadMinus"
m_arrButtonCode(87) = 72 : m_arrButtonKey(87) = "Keypad7Home"
m_arrButtonCode(88) = 73 : m_arrButtonKey(88) = "Keypad8Up"
m_arrButtonCode(89) = 74 : m_arrButtonKey(89) = "Keypad9PgUp"
m_arrButtonCode(90) = 79 : m_arrButtonKey(90) = "KeypadPlus"
m_arrButtonCode(91) = 76 : m_arrButtonKey(91) = "Keypad4Left"
m_arrButtonCode(92) = 77 : m_arrButtonKey(92) = "Keypad5"
m_arrButtonCode(93) = 78 : m_arrButtonKey(93) = "Keypad6Right"
m_arrButtonCode(94) = 80 : m_arrButtonKey(94) = "Keypad1End"
m_arrButtonCode(95) = 81 : m_arrButtonKey(95) = "Keypad2Down"
m_arrButtonCode(96) = 82 : m_arrButtonKey(96) = "Keypad3PgDn"
m_arrButtonCode(97) = 285 : m_arrButtonKey(97) = "KeypadEnter"
m_arrButtonCode(98) = 83 : m_arrButtonKey(98) = "Keypad0Ins"
m_arrButtonCode(99) = 84 : m_arrButtonKey(99) = "KeypadPeriodDel"
' not sure if this works:
'' CODE(S) DETECTED WITH _KEYDOWN:
'm_arrButtonCode(100) = -1 : m_arrButtonCode(100) = "F10"
' not sure if this works:
'' CODE(S) DETECTED WITH _KEYHIT:
'm_arrButtonCode(101) = -2 : m_arrButtonCode(101) = "AltLeft"
'm_arrButtonCode(102) = -3 : m_arrButtonCode(102) = "AltRight"
' DESCRIPTIONS BY KEYCODE
m_arrButtonKeyDesc(iLoop) = ""
m_arrButtonKeyDesc(2) = "Esc"
m_arrButtonKeyDesc(60) = "F1"
m_arrButtonKeyDesc(61) = "F2"
m_arrButtonKeyDesc(62) = "F3"
m_arrButtonKeyDesc(63) = "F4"
m_arrButtonKeyDesc(64) = "F5"
m_arrButtonKeyDesc(65) = "F6"
m_arrButtonKeyDesc(66) = "F7"
m_arrButtonKeyDesc(67) = "F8"
m_arrButtonKeyDesc(68) = "F9"
m_arrButtonKeyDesc(88) = "F11"
m_arrButtonKeyDesc(89) = "F12"
m_arrButtonKeyDesc(42) = "Tilde"
m_arrButtonKeyDesc(3) = "1"
m_arrButtonKeyDesc(4) = "2"
m_arrButtonKeyDesc(5) = "3"
m_arrButtonKeyDesc(6) = "4"
m_arrButtonKeyDesc(7) = "5"
m_arrButtonKeyDesc(8) = "6"
m_arrButtonKeyDesc(9) = "7"
m_arrButtonKeyDesc(10) = "8"
m_arrButtonKeyDesc(11) = "9"
m_arrButtonKeyDesc(12) = "0"
m_arrButtonKeyDesc(13) = "Minus"
m_arrButtonKeyDesc(14) = "Equal"
m_arrButtonKeyDesc(15) = "BkSp"
m_arrButtonKeyDesc(16) = "Tab"
m_arrButtonKeyDesc(17) = "Q"
m_arrButtonKeyDesc(18) = "W"
m_arrButtonKeyDesc(19) = "E"
m_arrButtonKeyDesc(20) = "R"
m_arrButtonKeyDesc(21) = "T"
m_arrButtonKeyDesc(22) = "Y"
m_arrButtonKeyDesc(23) = "U"
m_arrButtonKeyDesc(24) = "I"
m_arrButtonKeyDesc(25) = "O"
m_arrButtonKeyDesc(26) = "P"
m_arrButtonKeyDesc(27) = "BracketLeft"
m_arrButtonKeyDesc(28) = "BracketRight"
m_arrButtonKeyDesc(44) = "Backslash"
m_arrButtonKeyDesc(59) = "CapsLock"
m_arrButtonKeyDesc(31) = "A"
m_arrButtonKeyDesc(32) = "S"
m_arrButtonKeyDesc(33) = "D"
m_arrButtonKeyDesc(34) = "F"
m_arrButtonKeyDesc(35) = "G"
m_arrButtonKeyDesc(36) = "H"
m_arrButtonKeyDesc(37) = "J"
m_arrButtonKeyDesc(38) = "K"
m_arrButtonKeyDesc(39) = "L"
m_arrButtonKeyDesc(40) = "Semicolon"
m_arrButtonKeyDesc(41) = "Apostrophe"
m_arrButtonKeyDesc(29) = "Enter"
m_arrButtonKeyDesc(43) = "ShiftLeft"
m_arrButtonKeyDesc(45) = "Z"
m_arrButtonKeyDesc(46) = "X"
m_arrButtonKeyDesc(47) = "C"
m_arrButtonKeyDesc(48) = "V"
m_arrButtonKeyDesc(49) = "B"
m_arrButtonKeyDesc(50) = "N"
m_arrButtonKeyDesc(51) = "M"
m_arrButtonKeyDesc(52) = "Comma"
m_arrButtonKeyDesc(53) = "Period"
m_arrButtonKeyDesc(54) = "Slash"
m_arrButtonKeyDesc(55) = "ShiftRight"
m_arrButtonKeyDesc(30) = "CtrlLeft"
m_arrButtonKeyDesc(348) = "WinLeft"
m_arrButtonKeyDesc(58) = "Spacebar"
m_arrButtonKeyDesc(349) = "WinRight"
m_arrButtonKeyDesc(350) = "Menu"
m_arrButtonKeyDesc(286) = "CtrlRight"
m_arrButtonKeyDesc(339) = "Ins"
m_arrButtonKeyDesc(328) = "Home"
m_arrButtonKeyDesc(330) = "PgUp"
m_arrButtonKeyDesc(340) = "Del"
m_arrButtonKeyDesc(336) = "End"
m_arrButtonKeyDesc(338) = "PgDn"
m_arrButtonKeyDesc(329) = "Up"
m_arrButtonKeyDesc(332) = "Left"
m_arrButtonKeyDesc(337) = "Down"
m_arrButtonKeyDesc(334) = "Right"
m_arrButtonKeyDesc(71) = "ScrollLock"
m_arrButtonKeyDesc(326) = "NumLock"
m_arrButtonKeyDesc(310) = "KeypadSlash"
m_arrButtonKeyDesc(56) = "KeypadMultiply"
m_arrButtonKeyDesc(75) = "KeypadMinus"
m_arrButtonKeyDesc(72) = "Keypad7Home"
m_arrButtonKeyDesc(73) = "Keypad8Up"
m_arrButtonKeyDesc(74) = "Keypad9PgUp"
m_arrButtonKeyDesc(79) = "KeypadPlus"
m_arrButtonKeyDesc(76) = "Keypad4Left"
m_arrButtonKeyDesc(77) = "Keypad5"
m_arrButtonKeyDesc(78) = "Keypad6Right"
m_arrButtonKeyDesc(80) = "Keypad1End"
m_arrButtonKeyDesc(81) = "Keypad2Down"
m_arrButtonKeyDesc(82) = "Keypad3PgDn"
m_arrButtonKeyDesc(285) = "KeypadEnter"
m_arrButtonKeyDesc(83) = "Keypad0Ins"
m_arrButtonKeyDesc(84) = "KeypadPeriodDel"
End Sub ' InitKeyboardButtonCodes
' /////////////////////////////////////////////////////////////////////////////
' not sure if this works
' Returns TRUE if the F10 key is held down.
' We use _KEYDOWN for this because _BUTTON doesn't detect F10.
' Constant must be declared globally:
' Const c_iKeyDown_F10 = 17408
'_KEYCLEAR: _DELAY 1
KeydownF10% = TRUE
KeydownF10% = FALSE
'_KEYCLEAR
' /////////////////////////////////////////////////////////////////////////////
' not sure if this works
' Returns TRUE if the left ALT key is held down.
' We use _KEYHIT for this because _BUTTON doesn't detect ALT.
' Constant must be declared globally:
' Const c_iKeyHit_AltLeft = -30764
'_KEYCLEAR: _DELAY 1
KeyhitAltLeft% = TRUE
KeyhitAltLeft% = FALSE
'_KEYCLEAR
' /////////////////////////////////////////////////////////////////////////////
' not sure if this works
' Returns TRUE if the right ALT key is held down.
' We use _KEYHIT for this because _BUTTON doesn't detect ALT.
' Constant must be declared globally:
' Const c_iKeyHit_AltRight = -30765
'_KEYCLEAR: _DELAY 1
KeyhitAltRight% = TRUE
KeyhitAltRight% = FALSE
'_KEYCLEAR
' /////////////////////////////////////////////////////////////////////////////
' DEVICES Button
' _LASTBUTTON(1) keyboards will normally return 512 buttons. One button is read per loop through all numbers.
' _BUTTONCHANGE(number) returns -1 when pressed, 1 when released and 0 when there is no event since the last read.
' _BUTTON(number) returns -1 when a button is pressed and 0 when released
' Detects most keys (where the codes are documented?)
' However, does not seem to detect:
' Key Use
' --- ---
' F10 Function KeydownF10%
' Left Alt Function KeyhitAltLeft%
' Right Alt Function KeyhitAltRight%
' Print Screen (system API call?)
' Pause/Break (system API call?)
'_KEYCLEAR: _DELAY 1
KeyPressed% = TRUE
KeyPressed% = FALSE
'_KEYCLEAR
' /////////////////////////////////////////////////////////////////////////////
Dim RoutineName
As String: RoutineName
= "TestJoysticks$"
' 1 is the keyboard
' 2 is the mouse
' 3 is the joystick
' unless someone has a strange setup with multiple mice/keyboards/ect...
' In that case, you can use _DEVICE$(i) to look for "KEYBOARD", "MOUSE", "JOYSTICK", if necessary.
' I've never actually found it necessary, but I figure it's worth mentioning, just in case...
iDeviceCount
= _DEVICES ' Find the number of devices on someone's system TestJoysticks1
sResult = ""
sResult = "No joysticks found."
TestJoysticks$ = sResult
' /////////////////////////////////////////////////////////////////////////////
' Reads controllers and displays values on screen.
' Currently this is set up to support up to 8 joysticks,
' with upto 4 buttons and 2 axes each
' Testing with an old USB Logitech RumblePad 2
' and Atari 2600 joysticks plugged into using
' iCode Atari Joystick, Paddle, Driving to USB Adapter 4 ports
DIM RoutineName
AS STRING:: RoutineName
= "TestJoysticks1"
DIM arrButton
(32, 16) As Integer ' number of buttons on the joystick DIM arrButtonMin
(32, 16) As Integer ' stores the minimum value read DIM arrButtonMax
(32, 16) As Integer ' stores the maximum value read DIM arrAxis
(32, 16) As Double ' number of axis on the joystick DIM arrAxisMin
(32, 16) As Double ' stores the minimum value read DIM arrAxisMax
(32, 16) As Double ' stores the maximum value read DIM arrAxisAvg
(32, 16) As Double ' stores the average value read in the last few measurements DIM arrButtonNew
(32, 16) As Integer ' tracks when to initialize values DIM arrAxisNew
(32, 16) As Integer ' tracks when to initialize values
DIM arrController
(8) AS ControllerType
' holds info for each player DIM iDigits
AS INTEGER ' # digits to display (values are truncated to this length) 'DIM iMeasureCount AS INTEGER
' SET UP SCREEN
' INITIALIZE
iDigits = 4 ' 11
iColCount = 3
iColWidth = iCols \ iColCount
' COUNT # OF JOYSTICKS
' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
iDeviceCount
= _DEVICES ' Find the number of devices on someone's system PRINT "NO JOYSTICKS FOUND, EXITING..." INPUT "PRESS <ENTER>";in$
' BASE # OF PLAYERS ON HOW MANY CONTROLLERS FOUND
iNumControllers = iDeviceCount - 2 ' TODO: find out the right way to count joysticks
IF iNumControllers
> cMaxControllers
THEN iNumControllers = cMaxControllers
' INITIALIZE PLAYER COORDINATES AND SCREEN CHARACTERS
iNextY = 1
iNextX = -3
iNextC = 64
FOR iController
= 1 TO iNumControllers
iNextX = iNextX + 4
iNextX = 1
iNextY = iNextY + 4
iNextC = iNextC + 1
arrController(iController).buttonCount = cMaxButtons
arrController(iController).axisCount = cMaxAxis
for iLoop
= 1 to cMaxButtons
arrButtonNew(iController, iLoop) = TRUE
for iLoop
= 1 to cMaxAxis
arrAxisNew(iController, iLoop) = TRUE
arrAxisAvg(iController, iLoop) = 0
' CLEAR THE SCREEN
'iMeasureCount = 0
FOR iController
= 1 TO iNumControllers
iDevice = iController + 2
''IF _DEVICEINPUT = 3 THEN ' this says we only care about joystick input values
' check all the buttons
arrController(iController).buttonCount = iLoop
' update button array to indicate if a button is up or down currently.
'' _BUTTON(number) returns -1 when a button is pressed and 0 when released.
''arrButton(iLoop) = NOT arrButton(iLoop)
arrButton
(iController
, iLoop
) = _BUTTON(iLoop
)
'' SAVE MINIMUM VALUE
'if arrButton(iController, iLoop) < arrButtonMin(iController, iLoop) then
' arrButtonMin(iController, iLoop) = arrButton(iController, iLoop)
'
' ' INITIALIZE THE MAX TO THE MINIMUM VALUE
' IF arrButtonNew(iController, iLoop) = TRUE THEN
' arrButtonMax(iController, iLoop) = arrButtonMin(iController, iLoop)
' arrButtonNew(iController, iLoop) = FALSE
' END IF
'end if
'
'' SAVE MAXIMUM VALUE
'if arrButton(iController, iLoop) > arrButtonMax(iController, iLoop) then
' arrButtonMax(iController, iLoop) = arrButton(iController, iLoop)
'end if
arrController(iController).axisCount = iLoop
' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
' which is prone to always give minute values and never really center on true 0.
' A value of 1 means my axis is pushed fully in one direction.
' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
dblNextAxis
= _AXIS(iLoop
) dblNextAxis = RoundUpDouble# (dblNextAxis, 3)
'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
arrAxis(iController, iLoop) = dblNextAxis
arrAxis(iController, iLoop) = 0
'' SAVE MINIMUM VALUE
'if arrAxis(iController, iLoop) < arrAxisMin(iController, iLoop) then
' arrAxisMin(iController, iLoop) = arrAxis(iController, iLoop)
'
' ' INITIALIZE THE MAX TO THE MINIMUM VALUE
' IF arrAxisNew(iController, iLoop) = TRUE THEN
' arrAxisMax(iController, iLoop) = arrAxisMin(iController, iLoop)
' arrAxisNew(iController, iLoop) = FALSE
' END IF
'end if
'
'' SAVE MAXIMUM VALUE
'if arrAxis(iController, iLoop) > arrAxisMax(iController, iLoop) then
' arrAxisMax(iController, iLoop) = arrAxis(iController, iLoop)
'end if
'
'' ADD CURRENT VALUE TO AVERAGE SUM
'arrAxisAvg(iController, iLoop) = arrAxisAvg(iController, iLoop) + arrAxis(iController, iLoop)
WEND ' clear and update the device buffer
'PRINT "*** iNumControllers=" + cstr$(iNumControllers) + " ***"
'iMeasureCount = iMeasureCount + 1
'if iMeasureCount = 10 then
'iMeasureCount = 0
' And below here is just the simple display routine which displays our values.
' If this was for a game, I'd choose something like arrAxis(1) = -1 for a left arrow style input,
' arrAxis(1) = 1 for a right arrow style input, rather than just using _KEYHIT or INKEY$.
InitColumns iColCount
m_StartRow = 6
m_EndRow = iRows - 2
'm_StartCol
'm_EndCol
PrintString 1, 1, "Game controller test program."
PrintString 1, 2, "This program is free to use and distribute per GNU GPLv3 license."
PrintString 1, 3, "Tests up to 4 controllers with 2 axes / 2 buttons each."
PrintString 1, 4, "Plug in controllers and move them & press buttons."
PrintString 1, 5, "-------------------------------------------------------------------------------"
iGroupCount = 0
FOR iController
= 1 TO iNumControllers
FOR iLoop
= 1 TO arrController
(iController
).axisCount
' A loop for each axis strAxis
= right$(" " + cstr$
(iLoop
), 2)
sLine = ""
' display their status to the screen
sLine = sLine + "Player " + cstr$(iController)
strValue = FormatNumber$(arrAxis(iController, iLoop), iDigits)
sLine = sLine + ", Axis #" + strAxis + " = " + strValue
'strValue = FormatNumber$(arrAxisMin(iController, iLoop), iDigits)
'sLine = sLine + ", Min=" + strValue
'
'strValue = FormatNumber$(arrAxisMax(iController, iLoop), iDigits)
'sLine = sLine + ", Max=" + strValue
'
'' COMPUTE AVERAGE
'dblAverage = arrAxisAvg(iController, iLoop) / 10
'dblAverage = RoundUpDouble# (dblAverage, 3)
'strValue = FormatNumber$(dblAverage, iDigits)
'sLine = sLine + ", Avg=" + strValue
'
'' CLEAR THE AVERAGE
'arrAxisAvg(iController, iLoop) = 0
PrintColumn sLine
FOR iLoop
= 1 TO arrController
(iController
).buttonCount
' A loop for each button strAxis
= right$(" " + cstr$
(iLoop
), 2)
sLine = ""
' display their status to the screen
sLine = sLine + "Player " + cstr$(iController)
strValue = FormatNumber$(arrButton(iController, iLoop), iDigits)
sLine = sLine + ", Button #" + strAxis + " = " + strValue
'strValue = FormatNumber$(arrButtonMin(iController, iLoop), iDigits)
'sLine = sLine + ", Min=" + strValue
'
'strValue = FormatNumber$(arrButtonMax(iController, iLoop), iDigits)
'sLine = sLine + ", Max=" + strValue
PrintColumn sLine
iGroupCount = iGroupCount + 1
ColumnBreak
iGroupCount = 0
PrintString 1, iRows - 1, "-------------------------------------------------------------------------------"
PrintString 1, iRows - 0, "PRESS <ESC> TO EXIT"
'end if
' RETURN TO TEXT SCREEN
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN FILE FUNCTIONS
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' /////////////////////////////////////////////////////////////////////////////
' File format is comma-delimited
' containing controller info for one action per line
' where each line contains the following in this order:
' TAB ORDER INFO TYPE DESCRIPTION
' 1 {player #} Integer player # this mapping is for
' 2 {which action} Integer which action this mapping is for (up/down/right/left/button 1/button 2, etc.)
' 3 {device #} Integer number of the device this is mapped to
' 4 {type} Integer type of input (one of: cInputKey, cInputButton, cInputAxis)
' 5 {which} Integer if button the _BUTTON #, if axis the _AXIS #, if keyboard the _BUTTON #
' 6 {value} Integer if axis, the value (-1 or 1), else can be ignored
' These need to be declared globally and populated:
' ReDim Shared m_arrControlMap(1 To 8, 1 To 8) As ControlInputType
' Dim Shared m_ControlMapFileName$: m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
' If there is an error, returns error message,
' else returns blank string.
Dim RoutineName
As String:: RoutineName
= "SaveControllerMap$" 'Dim iError As Long: iError = 0
DebugPrint "--------------------------------------------------------------------------------"
DebugPrint "Started " + RoutineName
DebugPrint "--------------------------------------------------------------------------------"
' Get file name
m_ControlMapFileName$
= Left$(m_ProgramName$
, _InStrRev(m_ProgramName$
, ".")) + "map.txt" sFile
= Mid$(m_ControlMapFileName$
, _InStrRev(m_ControlMapFileName$
, "\") + 1)
'_KeyClear
'Cls
'Print "SAVE CONTROLLER MAPPING:"
'Print "Default file name is " + Chr$(34) + m_ControlMapFileName$ + Chr$(34) + "."
'Input "Type save file name, or blank for default: ", in$
'in$ = _Trim$(in$)
'If Len(in$) > 0 Then
' m_ControlMapFileName$ = in$
'End If
'sFile = m_ProgramPath$ + m_ControlMapFileName$
DebugPrint
"m_ControlMapFileName$=" + CHR$(34) + m_ControlMapFileName$
+ CHR$(34)
' Save mapping to file
sLine = ""
sLine = sLine + sDelim
sLine = sLine + sDelim
sLine
= sLine
+ _Trim$(Str$(m_arrControlMap
(iPlayer
, iWhichInput
).device
)) sLine = sLine + sDelim
sLine
= sLine
+ _Trim$(Str$(m_arrControlMap
(iPlayer
, iWhichInput
).typ
)) sLine = sLine + sDelim
sLine
= sLine
+ _Trim$(Str$(m_arrControlMap
(iPlayer
, iWhichInput
).code
)) sLine = sLine + sDelim
sLine
= sLine
+ _Trim$(Str$(m_arrControlMap
(iPlayer
, iWhichInput
).value
))
iCount = iCount + 1
DebugPrint
"Wrote " + _Trim$(Str$(iCount
)) + " lines." 'Print "Skipped " + _Trim$(Str$(iError)) + " lines."
DebugPrint ""
'Input "PRESS <ENTER> TO CONTINUE", in$
sResult
= "Saved mapping file " + chr$(34) + sFile
+ chr$(34) + "." sResult = "ERRORS: " + sError
SaveControllerMap$ = sResult
' /////////////////////////////////////////////////////////////////////////////
Dim RoutineName
As String:: RoutineName
= "LoadControllerMap$"
'Dim sDebugLine As String
DebugPrint "--------------------------------------------------------------------------------"
DebugPrint "Started " + RoutineName
DebugPrint "--------------------------------------------------------------------------------"
' Get file name
m_ControlMapFileName$
= Left$(m_ProgramName$
, _InStrRev(m_ProgramName$
, ".")) + "map.txt" sFile
= Mid$(m_ControlMapFileName$
, _InStrRev(m_ControlMapFileName$
, "\") + 1)
'' Get file name
'If Len(sError) = 0 Then
' Cls
' If Len(m_ControlMapFileName$) = 0 Then
' m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
' End If
' Print "LOAD CONTROLLER MAPPING:"
' Print "Default file name is " + Chr$(34) + m_ControlMapFileName$ + Chr$(34) + "."
' Input "Type name of file to open, or blank for default: ", in$
' in$ = _Trim$(in$)
' If Len(in$) > 0 Then
' m_ControlMapFileName$ = in$
' End If
' sFile = m_ProgramPath$ + m_ControlMapFileName$
'End If
' Make sure file exists
sError
= "File not found: " + Chr$(34) + m_ControlMapFileName$
+ Chr$(34) DebugPrint
"Found file: " + chr$(34) + m_ControlMapFileName$
+ chr$(34)
' Read data from file
DebugPrint "OPEN m_ControlMapFileName$ FOR BINARY AS #1"
iTotal
= Len(sText
) - Len(Replace$
(sText
, Chr$(13), "")) sText = ""
'INPUT #1, sLine
Line Input #1, sLine
' read entire text file line
iRead = iRead + 1
DebugPrint
"Parsing line " + _Trim$(Str$(iRead
)) + _
sLine = Replace$(sLine, " ", "") ' Remove spaces
sLine
= Replace$
(sLine
, Chr$(9), "") ' Remove tabs sLine
= Replace$
(sLine
, Chr$(10), "") ' Remove line breaks sLine
= Replace$
(sLine
, Chr$(13), "") ' Remove carriage returns DebugPrint
" Trimmed=" + chr$(34) + sLine
+ chr$(34)
split sLine, ",", arrNextLine()
DebugPrint "split into arrNextLine()"
iNumValues
= (ubound(arrNextLine
) - lbound(arrNextLine
)) + 1 iAdjust = -1 '- lbound(arrNextLine)
if IsNum%
(arrNextLine
(1 + iAdjust
)) = TRUE
then iPlayer
= VAL(arrNextLine
(1 + iAdjust
)) sNextErr = "Error on line " + cstr$(iRead) + ", value 1: not a number"
if IsNum%
(arrNextLine
(2 + iAdjust
)) = TRUE
then iWhichInput
= VAL(arrNextLine
(2 + iAdjust
)) sNextErr = "Error on line " + cstr$(iRead) + ", value 2: not a number"
if IsNum%
(arrNextLine
(3 + iAdjust
)) = TRUE
then iDevice
= VAL(arrNextLine
(3 + iAdjust
)) sNextErr = "Error on line " + cstr$(iRead) + ", value 3: not a number"
if IsNum%
(arrNextLine
(4 + iAdjust
)) = TRUE
then iType
= VAL(arrNextLine
(4 + iAdjust
)) sNextErr = "Error on line " + cstr$(iRead) + ", value 4: not a number"
if IsNum%
(arrNextLine
(5 + iAdjust
)) = TRUE
then iWhich
= VAL(arrNextLine
(5 + iAdjust
)) sNextErr = "Error on line " + cstr$(iRead) + ", value 5: not a number"
if IsNum%
(arrNextLine
(6 + iAdjust
)) = TRUE
then iValue
= VAL(arrNextLine
(6 + iAdjust
)) sNextErr = "Error on line " + cstr$(iRead) + ", value 6: not a number"
' validate iPlayer
sNextErr
= "Player value " + _Trim$(Str$(iPlayer
)) + _
" is outside lbound(m_arrControlMap, 1) " + _
sNextErr
= "Player value " + _Trim$(Str$(iPlayer
)) + _
" is outside ubound(m_arrControlMap, 1) " + _
' validate iWhichInput
sNextErr
= "WhichInput value " + _Trim$(Str$(iWhichInput
)) + _
" is outside lbound(m_arrControlMap, 2) " + _
sNextErr
= "WhichInput value " + _Trim$(Str$(iWhichInput
)) + _
" is outside ubound(m_arrControlMap, 2) " + _
sNextErr = "Error on line " + cstr$(iRead) + ": detected " + cstr$(iNumValues) + " values, expected 6."
iValid = iValid + 1
m_arrControlMap(iPlayer, iWhichInput).device = iDevice
m_arrControlMap(iPlayer, iWhichInput).typ = iType
m_arrControlMap(iPlayer, iWhichInput).code = iWhich
m_arrControlMap(iPlayer, iWhichInput).value = iValue
iBad = iBad + 1
DebugPrint sNextErr
DebugPrint " Line is blank: skipped"
iBlank = iBlank + 1
DebugPrint ""
DebugPrint ""
'Input "PRESS <ENTER> TO CONTINUE", in$
sResult
= "Loaded mapping file " + chr$(34) + sFile
+ chr$(34) + "." sResult = "ERRORS: " + sError
LoadControllerMap$ = sResult
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END FILE FUNCTIONS
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN GRAPHIC PRINTING ROUTINES
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' /////////////////////////////////////////////////////////////////////////////
' Eliminates the math.
' Text resolution:
' 648 x 480: 80 x 30
' 720 x 480: 90 x 30
' 800 x 600: 100 x 37
' 1024 x 768: 128 x 48
' 1280 x 1024: 160 x 64
' 1920 x 1080: 240 x 67
' 2048 x 1152: 256 x 72 (truncated after 70 rows, 255 columns)
' 3840 x 2160: 480 x135 (truncated after 133 rows, 479 columns)
' /////////////////////////////////////////////////////////////////////////////
' A way to automatically print to columns.
' Depends on the following shared variables:
' Dim Shared m_NumColumns As Integer : m_NumColumns = 1
' Dim Shared m_PrintRow As Integer : m_PrintRow = 0
' Dim Shared m_PrintCol As Integer : m_PrintCol = 0
' Dim Shared m_StartRow As Integer : m_StartRow = 0
' Dim Shared m_EndRow As Integer : m_EndRow = 0
' Dim Shared m_StartCol As Integer : m_StartCol = 0
' Dim Shared m_EndCol As Integer : m_EndCol = 0
' InitColumns 2
' m_PrintRow = 5
' m_PrintCol = 2
' PrintColumn "Col 2, Row 5"
' PrintColumn "m_NumColumns=" + cstr$(m_NumColumns)
if m_NumColumns
< 1 or m_NumColumns
> iCols
then m_NumColumns = 1
if m_StartRow
< 1 or m_StartRow
> iRows
then m_StartRow = 1
if m_EndRow
< m_StartRow
or m_EndRow
> iRows
then m_EndRow = iRows
if m_StartCol
< 1 or m_StartCol
> m_NumColumns
then m_StartCol = 1
if m_EndCol
< m_StartCol
or m_EndCol
> m_NumColumns
then m_EndCol = m_NumColumns
if m_PrintRow
< m_StartRow
then m_PrintRow = m_StartRow
if m_PrintCol
< m_StartCol
then m_PrintCol = m_StartCol
iColWidth = iCols \ m_NumColumns
IF m_PrintRow
<= m_EndRow
THEN IF m_PrintCol
<= m_EndCol
THEN split MyString
, chr$(13), arrLines
() sLine
= left$(arrLines
(iRow
), iColWidth
) 'TODO: wrap remaining text
m_PrintRow = m_PrintRow + 1
m_PrintRow = m_StartRow
m_PrintCol = m_PrintCol + 1
if m_PrintCol
> m_NumColumns
then 'TODO: options for when we reach the bottom of the last column (stop printing, wrap around)
m_PrintCol = 1
' /////////////////////////////////////////////////////////////////////////////
' A way to automatically print to columns.
m_PrintRow = m_StartRow
m_PrintCol = m_PrintCol + 1
if m_PrintCol
> m_NumColumns
then 'TODO: options for when we go past the last column (stop printing, wrap around)
' /////////////////////////////////////////////////////////////////////////////
' A way to automatically print to columns.
if iNumColumns
< 1 or iNumColumns
> iCols
then m_NumColumns = 1
m_NumColumns = iNumColumns
if m_StartRow
< 1 or m_StartRow
> iRows
then m_StartRow = 1
if m_EndRow
< m_StartRow
or m_EndRow
> iRows
then m_EndRow = iRows
if m_StartCol
< 1 or m_StartCol
> m_NumColumns
then m_StartCol = 1
if m_EndCol
< m_StartCol
or m_EndCol
> m_NumColumns
then m_EndCol = m_NumColumns
m_PrintRow = 1
m_PrintCol = 1
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END GRAPHIC PRINTING ROUTINES
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN DEBUGGING ROUTINES #DEBUGGING
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'ReDim arrLines$(0)
'dim delim$ : delim$ = Chr$(13)
'split MyString, delim$, arrLines$()
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END DEBUGGING ROUTINES @DEBUGGING
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' BEGIN GENERAL PURPOSE ROUTINES #GEN
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' /////////////////////////////////////////////////////////////////////////////
' Convert a value to string and trim it (because normal Str$ adds spaces)
'cstr$ = LTRIM$(RTRIM$(STR$(myValue)))
' /////////////////////////////////////////////////////////////////////////////
' Convert a Long value to string and trim it (because normal Str$ adds spaces)
' /////////////////////////////////////////////////////////////////////////////
' Convert a Single value to string and trim it (because normal Str$ adds spaces)
''cstr$ = LTRIM$(RTRIM$(STR$(myValue)))
' /////////////////////////////////////////////////////////////////////////////
' Convert an unsigned Long value to string and trim it (because normal Str$ adds spaces)
' /////////////////////////////////////////////////////////////////////////////
' Scientific notation - QB64 Wiki
' https://www.qb64.org/wiki/Scientific_notation
' Example: A string function that displays extremely small or large exponential decimal values.
Xpos%
= INSTR(value$
, "D") + INSTR(value$
, "E") 'only D or E can be present expo%
= VAL(MID$(value$
, Xpos%
+ 1)) sign$
= "-": valu$
= MID$(value$
, 2, Xpos%
- 2) dot%
= INSTR(valu$
, "."): L%
= LEN(valu$
) DblToStr$
= _TRIM$( sign$
+ DP$
+ min$
+ num$
+ add$
)
' /////////////////////////////////////////////////////////////////////////////
strValue
= DblToStr$
(myValue
) + STRING$(iDigits
, " ") strValue = Replace$(strValue, "-.", "-0.")
strValue = "0" + strValue
FormatNumber$
= LEFT$(strValue
, iDigits
)
' /////////////////////////////////////////////////////////////////////////////
' From: Bitwise Manipulations By Steven Roman
' http://www.romanpress.com/Articles/Bitwise_R/Bitwise.htm
' Returns the 8-bit binary representation
' of an integer iInput where 0 <= iInput <= 255
sResult = ""
iInput = iInput \ 2
'If iLoop = 4 Then sResult = " " + sResult
GetBinary$ = sResult
' /////////////////////////////////////////////////////////////////////////////
' wonderfully inefficient way to read if a bit is set
' ival = GetBit256%(int we are comparing, int containing the bits we want to read)
' See also: GetBit256%, SetBit256%
'DIM iTemp AS INTEGER
iResult = FALSE
bContinue = TRUE
sNum = GetBinary$(iNum)
sBit = GetBinary$(iBit)
'if any of the bits in iBit are false, return false
iResult = FALSE
bContinue = FALSE
iResult = TRUE
GetBit256% = iResult
' /////////////////////////////////////////////////////////////////////////////
' From: Bitwise Manipulations By Steven Roman
' http://www.romanpress.com/Articles/Bitwise_R/Bitwise.htm
' Returns the integer that corresponds to a binary string of length 8
iResult = 0
strBinary = Replace$(sBinary, " ", "") ' Remove any spaces
iResult
= iResult
+ 2 ^ iLoop
* Val(Mid$(strBinary
, Len(strBinary
) - iLoop
, 1))
GetIntegerFromBinary% = iResult
' /////////////////////////////////////////////////////////////////////////////
Function IIF
(Condition
, IfTrue
, IfFalse
)
' /////////////////////////////////////////////////////////////////////////////
Function IIFSTR$
(Condition
, IfTrue$
, IfFalse$
) If Condition
Then IIFSTR$
= IfTrue$
Else IIFSTR$
= IfFalse$
' /////////////////////////////////////////////////////////////////////////////
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/
IsEven% = TRUE
IsEven% = FALSE
' /////////////////////////////////////////////////////////////////////////////
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/
IsOdd% = TRUE
IsOdd% = FALSE
' /////////////////////////////////////////////////////////////////////////////
' By sMcNeill from https://www.qb64.org/forum/index.php?topic=896.0
IsNum% = TRUE
IsNum% = FALSE
' /////////////////////////////////////////////////////////////////////////////
' Re: Does a Is Number function exist in QB64?
' https://www.qb64.org/forum/index.php?topic=896.15
' MWheatley
' « Reply #18 on: January 01, 2019, 11:24:30 AM »
' returns 1 if string is an integer, 0 if not
IsNumber = 1
IsNumber = 0
IsNumber = 0
' /////////////////////////////////////////////////////////////////////////////
' Split and join strings
' https://www.qb64.org/forum/index.php?topic=1073.0
'Combine all elements of in$() into a single string with delimiter$ separating the elements.
result$ = result$ + delimiter$ + in$(i)
join$ = result$
' /////////////////////////////////////////////////////////////////////////////
' ABS was returning strange values with type LONG
' so I created this which does not.
LongABS& = 0 - lngValue
LongABS& = lngValue
' /////////////////////////////////////////////////////////////////////////////
' Writes sText to a debug file in the EXE folder.
' Debug file is named the same thing as the program EXE name with ".txt" at the end.
' For example the program "C:\QB64\MyProgram.BAS" running as
' "C:\QB64\MyProgram.EXE" would have an output file "C:\QB64\MyProgram.EXE.txt".
' If the file doesn't exist, it is created, otherwise it is appended to.
sFileName = ProgramPath$ + ProgramName$ + ".txt"
sError = ""
sOut = ""
sOut
= sOut
+ "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10) sOut
= sOut
+ "PROGRAM : " + ProgramName$
+ Chr$(13) + Chr$(10) sOut
= sOut
+ "RUN DATE: " + CurrentDateTime$
+ Chr$(13) + Chr$(10) sOut
= sOut
+ "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10) sError = PrintFile$(sFileName, sOut, FALSE)
sError = PrintFile$(sFileName, sText, TRUE)
Print CurrentDateTime$
+ " DebugPrintFile FAILED: " + sError
' /////////////////////////////////////////////////////////////////////////////
' /////////////////////////////////////////////////////////////////////////////
' Returns blank if successful else returns error message.
'x = 1: y = 2: z$ = "Three"
sError = "Error in PrintFile$ : File not found. Cannot append."
Open sFileName
For Output As #1 ' opens and clears an existing file or creates new empty file ' WRITE places text in quotes in the file
'WRITE #1, x, y, z$
'WRITE #1, sText
' PRINT does not put text inside quotes
'PRINT "File created with data. Press a key!"
'K$ = INPUT$(1) 'press a key
'OPEN sFileName FOR INPUT AS #2 ' opens a file to read it
'INPUT #2, a, b, c$
'CLOSE #2
'PRINT a, b, c$
'WRITE a, b, c$
PrintFile$ = sError
' /////////////////////////////////////////////////////////////////////////////
' Generate random value between Min and Max.
' SET RANDOM SEED
'Randomize ' Initialize random-number generator.
' GET RANDOM # Min%-Max%
'RandomNumber = Int((Max * Rnd) + Min) ' generate number
NumSpread% = (Max% - Min%) + 1
RandomNumber%
= Int(Rnd * NumSpread%
) + Min%
' GET RANDOM # BETWEEN Max% AND Min%
' /////////////////////////////////////////////////////////////////////////////
sFileName = "c:\temp\maze_test_1.txt"
sText
= "Count" + Chr$(9) + "Min" + Chr$(9) + "Max" + Chr$(9) + "Random" bAppend = FALSE
sError = PrintFile$(sFileName, sText, bAppend)
bAppend = TRUE
iErrorCount = 0
iMin = 0
iMax = iCols - 1
iNum = RandomNumber%(iMin, iMax)
sError = PrintFile$(sFileName, sText, bAppend)
iErrorCount = iErrorCount + 1
Print " " + "Could not write to file " + Chr$(34) + sFileName
+ Chr$(34) + "."
iMin = 0
iMax = iRows - 1
iNum = RandomNumber%(iMin, iMax)
sError = PrintFile$(sFileName, sText, bAppend)
iErrorCount = iErrorCount + 1
Print " " + "Could not write to file " + Chr$(34) + sFileName
+ Chr$(34) + "."
Print "Finished generating numbers. Errors: " + Str$(iErrorCount
) Print "Error creating file " + Chr$(34) + sFileName
+ Chr$(34) + "."
Input "Press <ENTER> to continue", sInput$
' /////////////////////////////////////////////////////////////////////////////
' FROM: String Manipulation
' found at abandoned, outdated and now likely malicious qb64 dot net website
' http://www.qb64.[net]/forum/index_topic_5964-0/
'
'SUMMARY:
' Purpose: A library of custom functions that transform strings.
' Author: Dustinian Camburides (dustinian@gmail.com)
' Platform: QB64 (www.qb64.org)
' Revision: 1.6
' Updated: 5/28/2012
'SUMMARY:
'[Replace$] replaces all instances of the [Find] sub-string with the [Add] sub-string within the [Text] string.
'INPUT:
'Text: The input string; the text that's being manipulated.
'Find: The specified sub-string; the string sought within the [Text] string.
'Add: The sub-string that's being added to the [Text] string.
' VARIABLES:
Dim lngLocation
As Long ' The address of the [Find] substring within the [Text] string. Dim strBefore
As String ' The characters before the string to be replaced. Dim strAfter
As String ' The characters after the string to be replaced.
' INITIALIZE:
' MAKE COPIESSO THE ORIGINAL IS NOT MODIFIED (LIKE ByVal IN VBA)
Text2 = Text1
Find2 = Find1
Add2 = Add1
lngLocation
= InStr(1, Text2
, Find2
)
' PROCESSING:
' While [Find2] appears in [Text2]...
' Extract all Text2 before the [Find2] substring:
strBefore
= Left$(Text2
, lngLocation
- 1)
' Extract all text after the [Find2] substring:
strAfter
= Right$(Text2
, ((Len(Text2
) - (lngLocation
+ Len(Find2
) - 1))))
' Return the substring:
Text2 = strBefore + Add2 + strAfter
' Locate the next instance of [Find2]:
lngLocation
= InStr(1, Text2
, Find2
)
' Next instance of [Find2]...
' OUTPUT:
Replace$ = Text2
' /////////////////////////////////////////////////////////////////////////////
Print "-------------------------------------------------------------------------------"
in$ = "Thiz iz a teZt."
in$ = Replace$(in$, "z", "s")
in$ = Replace$(in$, "Z", "s")
Print "ReplaceTest finished."
' /////////////////////////////////////////////////////////////////////////////
' https://www.qb64.org/forum/index.php?topic=3605.0
' Quote from: SMcNeill on Today at 03:53:48 PM
'
' Sometimes, you guys make things entirely too complicated.
' There ya go! Three functions to either round naturally,
' always round down, or always round up, to whatever number of digits you desire.
' EDIT: Modified to add another option to round scientific,
' since you had it's description included in your example.
Round##
= INT(num##
* 10 ^ digits%
+ .5) / 10 ^ digits%
RoundUp##
= _CEIL(num##
* 10 ^ digits%
) / 10 ^ digits%
RoundDown##
= INT(num##
* 10 ^ digits%
) / 10 ^ digits%
FUNCTION Round_Scientific##
(num##
, digits%
) Round_Scientific##
= _ROUND(num##
* 10 ^ digits%
) / 10 ^ digits%
RoundUpDouble#
= _CEIL(num#
* 10 ^ digits%
) / 10 ^ digits%
RoundUpSingle!
= _CEIL(num!
* 10 ^ digits%
) / 10 ^ digits%
' /////////////////////////////////////////////////////////////////////////////
' fantastically inefficient way to set a bit
' example use: arrMaze(iX, iY) = SetBit256%(arrMaze(iX, iY), cS, FALSE)
' See also: GetBit256%, SetBit256%
' newint=SetBit256%(oldint, int containing the bits we want to set, value to set them to)
sNum = GetBinary$(iNum)
sBit = GetBinary$(iBit)
sVal = "1"
sVal = "0"
strResult = ""
strResult = strResult + sVal
strResult
= strResult
+ Mid$(sNum
, iLoop
, 1) iResult = GetIntegerFromBinary%(strResult)
iResult = iNum
SetBit256% = iResult
' /////////////////////////////////////////////////////////////////////////////
' Scientific notation - QB64 Wiki
' https://www.qb64.org/wiki/Scientific_notation
' Example: A string function that displays extremely small or large exponential decimal values.
Xpos%
= INSTR(value$
, "D") + INSTR(value$
, "E") 'only D or E can be present expo%
= VAL(MID$(value$
, Xpos%
+ 1)) sign$
= "-": valu$
= MID$(value$
, 2, Xpos%
- 2) dot%
= INSTR(valu$
, "."): L%
= LEN(valu$
) SngToStr$
= _TRIM$( sign$
+ DP$
+ min$
+ num$
+ add$
)
' /////////////////////////////////////////////////////////////////////////////
' Split and join strings
' https://www.qb64.org/forum/index.php?topic=1073.0
'
' FROM luke, QB64 Developer
' Date: February 15, 2019, 04:11:07 AM »
'
' Given a string of words separated by spaces (or any other character),
' splits it into an array of the words. I've no doubt many people have
' written a version of this over the years and no doubt there's a million
' ways to do it, but I thought I'd put mine here so we have at least one
' version. There's also a join function that does the opposite
' array -> single string.
'
' Code is hopefully reasonably self explanatory with comments and a little demo.
' Note, this is akin to Python/JavaScript split/join, PHP explode/implode.
'Split in$ into pieces, chopping at every occurrence of delimiter$. Multiple consecutive occurrences
'of delimiter$ are treated as a single instance. The chopped pieces are stored in result$().
'
'delimiter$ must be one character long.
'result$() must have been REDIMmed previously.
' Modified to handle multi-character delimiters
Sub split
(in$
, delimiter$
, result$
())
iDelimLen
= Len(delimiter$
)
start = 1
'While Mid$(in$, start, 1) = delimiter$
While Mid$(in$
, start
, iDelimLen
) = delimiter$
'start = start + 1
start = start + iDelimLen
finish
= InStr(start
, in$
, delimiter$
)
result$
(UBound(result$
)) = Mid$(in$
, start
, finish
- start
) start = finish + 1
' /////////////////////////////////////////////////////////////////////////////
in$ = "this" + delim$ + "is" + delim$ + "a" + delim$ + "test"
split in$, delim$, arrTest$()
Print "Split test finished."
' /////////////////////////////////////////////////////////////////////////////
Print "-------------------------------------------------------------------------------" Print "SplitAndReplaceTest"
in$
= "This line 1 " + Chr$(13) + Chr$(10) + "and line 2" + Chr$(10) + "and line 3 " + Chr$(13) + "finally THE END."
Print "Fixing linebreaks..."
split in$
, Chr$(13), arrTest$
()
Print "SplitAndReplaceTest finished."
' /////////////////////////////////////////////////////////////////////////////
StrPadRight$
= left$( sValue
+ STRING$(iWidth
, " "), iWidth
)
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' END GENERAL PURPOSE ROUTINES
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' #END
' ################################################################################################################################################################