Author Topic: WIP: game input mapping for gamepad, keyboard, v0.90  (Read 3693 times)

0 Members and 1 Guest are viewing this topic.

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
WIP: game input mapping for gamepad, keyboard, v0.90
« on: January 06, 2022, 05:01:11 pm »
Here's some code I have been working on, on and off for a while.
I needed some reusable simple way to map game input from USB game controllers and the keyboard, which could be saved to a config file and reused.
It's totally a WIP and there are currently a couple issues
  • The mapping part lets you map keys from the keyboard but for some reason the test routine that lets you move objects around the screen isn't reading the keyboard input.
  • Digital joysticks don't allow continuous movement.

Most other features are working and may come in handy.
I would be curious to hear any feedback or advice.
Enjoy
Code: QB64: [Select]
  1. ' ################################################################################################################################################################
  2. ' #TOP
  3.  
  4. ' Game Input Mapping Test
  5. ' Version 0.90 by madscijr
  6.  
  7. ' BASED ON CODE BY SMcNeill FROM:
  8. '     Simple Joystick Detection and Interaction  (Read 316 times)
  9. '     https://www.qb64.org/forum/index.php?topic=2160.msg129051#msg129051
  10. '     https://qb64forum.alephc.xyz/index.php?topic=2160.msg129083#msg129083
  11. ' and others (sources cited throughout).
  12.  
  13. ' ################################################################################################################################################################
  14. ' #CONSTANTS = GLOBAL CONSTANTS
  15.  
  16. ' boolean constants:
  17. Const FALSE = 0
  18. Const TRUE = Not FALSE
  19.  
  20. Const cInputNone = 0
  21. Const cInputKey = 1
  22. Const cInputButton = 2
  23. Const cInputAxis = 3
  24.  
  25. Const cMaxButtons = 12
  26. Const cMaxAxis = 8
  27. Const cMaxControllers = 8
  28. Const cMaxPlayers = 8
  29.  
  30. ' Use as index for array of ControlInputType
  31. Const cInputUp = 1
  32. Const cInputDown = 2
  33. Const cInputLeft = 3
  34. Const cInputRight = 4
  35. Const cInputButton1 = 5
  36. Const cInputButton2 = 6
  37. Const cInputButton3 = 7
  38. Const cInputButton4 = 8
  39.  
  40. Const c_iKeyDown_F10 = 17408
  41. Const c_iKeyHit_AltLeft = -30764
  42. Const c_iKeyHit_AltRight = -30765
  43.  
  44. ' ################################################################################################################################################################
  45. ' #UDT #TYPES = USER DEFINED TYPES
  46.  
  47. ' UDT TO HOLD THE INFO FOR A PLAYER
  48. TYPE PlayerType
  49.     x AS INTEGER ' player x position
  50.     y AS INTEGER ' player y position
  51.     c AS INTEGER ' character to display on screen
  52.     xOld AS INTEGER
  53.     yOld AS INTEGER
  54.    
  55.     moveX As Integer
  56.     lastMoveX As Integer
  57.     moveY As Integer
  58.     lastMoveY As Integer
  59.     button1 As Integer
  60.     lastButton1 As Integer
  61.     button2 As Integer
  62.     lastButton2 As Integer
  63.     button3 As Integer
  64.     lastButton3 As Integer
  65.     button4 As Integer
  66.     lastButton4 As Integer
  67.    
  68. END TYPE ' PlayerType
  69.  
  70. ' UDT TO HOLD THE INFO FOR A GAME CONTROLLER
  71. TYPE ControllerType
  72.     buttonCount AS INTEGER
  73.     axisCount AS INTEGER
  74. END TYPE ' ControllerType
  75.  
  76. ' UDT TO HOLD THE INFO FOR A GAME CONTROLLER
  77. TYPE ControlInputType
  78.     device As Integer
  79.     typ As Integer ' cInputKey, cInputButton, cInputAxis
  80.     code As Integer
  81.     value As Integer
  82. END TYPE ' ControlInputType
  83.  
  84. '' KEY MAPPING v1:
  85. 'Type DirKeyMapType
  86. '    KeyBack As Long
  87. '    KeyForward As Long
  88. '    KeyLeft As Long
  89. '    KeyRight As Long
  90. '    KeyUp As Long
  91. '    KeyDown As Long
  92. 'End Type ' DirKeyMapType
  93. '
  94. 'Type ColorTextType
  95. '    s as string
  96. '    fg As _Unsigned Long ' foreground color
  97. '    bg As _Unsigned Long ' background color
  98. 'End Type ' ColorTextType
  99. '
  100. 'Type xyByteType
  101. '    x as _byte
  102. '    y as _byte
  103. 'End Type ' xyByteType
  104. '
  105. 'Type xyzByteType
  106. '    x as _byte
  107. '    y as _byte
  108. '    z as _byte
  109. 'End Type ' xyzByteType
  110. '
  111. 'Type xyIntegerType
  112. '    x as integer
  113. '    y as integer
  114. 'End Type ' xyPointType
  115. '
  116. 'Type xyzIntegerType
  117. '    x as integer
  118. '    y as integer
  119. '    z as integer
  120. 'End Type ' xyzPointType
  121.  
  122. ' ################################################################################################################################################################
  123. ' #VARS = GLOBAL VARIABLES
  124.  
  125. Dim Shared m_ProgramPath$: m_ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
  126. Dim Shared m_ProgramName$: m_ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
  127. Dim Shared m_ControlMapFileName$: m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  128.  
  129. ' -----------------------------------------------------------------------------
  130. ' VARIABLES FOR GRAPHIC PRINTING ROUTINES
  131. ' -----------------------------------------------------------------------------
  132. Dim Shared m_NumColumns As Integer : m_NumColumns = 1
  133. Dim Shared m_PrintRow As Integer : m_PrintRow = 0
  134. Dim Shared m_PrintCol As Integer : m_PrintCol = 0
  135. Dim Shared m_StartRow As Integer : m_StartRow = 0
  136. Dim Shared m_EndRow As Integer : m_EndRow = 0
  137. Dim Shared m_StartCol As Integer : m_StartCol = 0
  138. Dim Shared m_EndCol As Integer : m_EndCol = 0
  139. ' -----------------------------------------------------------------------------
  140.  
  141. 'Dim Shared m_iMapMinX As Integer: m_iMapMinX = 0
  142. 'Dim Shared m_iMapMaxX As Integer: m_iMapMaxX = 64
  143. 'Dim Shared m_iMapMidX As Integer: m_iMapMidX = (m_iMapMaxX - m_iMapMinX) \ 2
  144. 'Dim Shared m_iMapMinY As Integer: m_iMapMinY = 0
  145. 'Dim Shared m_iMapMaxY As Integer: m_iMapMaxY = 64
  146. 'Dim Shared m_iMapMidY As Integer: m_iMapMidY = (m_iMapMaxY - m_iMapMinY) \ 2
  147. 'Dim Shared m_iMapMinZ As Integer: m_iMapMinZ = 0
  148. 'Dim Shared m_iMapMaxZ As Integer: m_iMapMaxZ = 64
  149. 'Dim Shared m_iMapMidZ As Integer: m_iMapMidZ = (m_iMapMaxZ - m_iMapMinZ) \ 2
  150. '
  151. 'Dim Shared m_iPlayerMin As Integer: m_iPlayerMin = 1
  152. 'Dim Shared m_iPlayerMax As Integer: m_iPlayerMax = 4
  153. 'Dim Shared m_iPlayerCount As Integer: m_iPlayerCount = 0
  154. 'Dim Shared m_iObjectCount As Integer: m_iObjectCount = 0 ' <- TO BE USED WHEN WE HAVE OBJECTS
  155. '
  156. 'Dim Shared m_arrMap(m_iMapMinX To m_iMapMaxX, m_iMapMinY To m_iMapMaxY, m_iMapMinZ To m_iMapMaxZ) As MapTileType
  157. 'Dim Shared m_arrRender0(m_iMapMinX To m_iMapMaxX, m_iMapMinY To m_iMapMaxY, m_iMapMinZ To m_iMapMaxZ) As MapTileType
  158. 'Dim Shared m_arrRender1(m_iMapMinX To m_iMapMaxX, m_iMapMinY To m_iMapMaxY, m_iMapMinZ To m_iMapMaxZ) As MapTileType
  159. 'Dim Shared m_arrRender2(m_iMapMinX To m_iMapMaxX, m_iMapMinY To m_iMapMaxY, m_iMapMinZ To m_iMapMaxZ) As MapTileType
  160. 'Dim Shared m_arrPlayer(m_iPlayerMin To m_iPlayerMax) As PlayerType
  161. '' For each player, map the 6 directional keys differently for each of the 6 directional orientations!
  162. 'Dim Shared m_arrDirKeyMap(m_iPlayerMin To m_iPlayerMax, c_iDir_Min To c_iDir_Max) As DirKeyMapType
  163.  
  164. ReDim Shared m_arrPlayer(1 To 8) As PlayerType ' holds info for each player
  165. ReDim Shared m_arrControlMap(1 To 8, 1 To 8) As ControlInputType ' holds control mapping for each player (player #, direction)
  166. ReDim Shared m_arrController(1 To 8) As ControllerType ' holds info for each game controller
  167.  
  168. ReDim Shared m_arrButtonCode(1 To 99) As Integer ' Long
  169. ReDim Shared m_arrButtonKey(1 To 99) As String
  170. ReDim Shared m_arrButtonKeyDesc(0 To 512) As String
  171.  
  172. ' ENABLE / DISABLE DEBUG CONSOLE
  173. Dim Shared m_bTesting As Integer : m_bTesting = TRUE
  174.  
  175. ' =============================================================================
  176. ' LOCAL VARIABLES
  177. Dim in$
  178.  
  179. ' ****************************************************************************************************************************************************************
  180. ' ACTIVATE DEBUGGING WINDOW
  181. IF m_bTesting = TRUE THEN
  182.     $Console
  183.     _Delay 4
  184.     _Console On
  185.     _Echo "Started " + m_ProgramName$
  186.     _Echo "Debugging on..."
  187. ' ****************************************************************************************************************************************************************
  188.  
  189. ' =============================================================================
  190. ' START THE MAIN ROUTINE
  191. main
  192.  
  193. ' =============================================================================
  194. ' FINISH
  195. Print m_ProgramName$ + " finished."
  196. Input "Press <ENTER> to continue", in$
  197.  
  198. ' ****************************************************************************************************************************************************************
  199. ' DEACTIVATE DEBUGGING WINDOW
  200. IF m_bTesting = TRUE THEN
  201. ' ****************************************************************************************************************************************************************
  202.  
  203. System ' return control to the operating system
  204.  
  205. ' /////////////////////////////////////////////////////////////////////////////
  206.  
  207. Sub main
  208.     Dim RoutineName As String: RoutineName = "main"
  209.     Dim in$
  210.     Dim result$ : result$ = ""
  211.  
  212.     ' SET UP SCREEN
  213.     Screen _NewImage(1280, 1024, 32): _ScreenMove 0, 0
  214.    
  215.     Do
  216.         If Len(result$) = 0 Then
  217.             Cls
  218.         Else
  219.             Print
  220.         End If
  221.        
  222.         Print m_ProgramName$
  223.         Print
  224.         Print "Game Input Mapping Test"
  225.         Print "v0.90, by Softintheheadware (Jan, 2022)"
  226.         Print
  227.        
  228.         Print "1. Basic controller test"
  229.         Print "2. Load controller mapping"
  230.         Print "3. View controller mapping"
  231.         Print "4. Edit  controller mapping for 1 or more players"
  232.         Print "   Note: not tested yet"
  233.         Print "5. Reset controller mapping for 1 or more players"
  234.         Print "6. Map controllers for 1 or more players"
  235.         Print "7. Test controller mappings to move around screen"
  236.         Print "   Issue #1: Keyboard input not working yet"
  237.         Print "   Issue #2: Digital joysticks don't allow continuous movement"
  238.         Print "   Issue #3: Need analog joystick sensitivity adjustment option"
  239.         Print "8. Save controller mappings"
  240.         Print
  241.         Print "What to do? ('q' to exit)"
  242.        
  243.         Input in$: in$ = LCase$(Left$(in$, 1))
  244.        
  245.         If in$ = "1" Then
  246.             result$ = TestJoysticks$
  247.         ElseIf in$ = "2" Then
  248.             result$ = LoadMappings$
  249.             if len(result$)=0 then result$="Loaded mappings."
  250.         ElseIf in$ = "3" Then
  251.             result$ = ViewMappings$
  252.         ElseIf in$ = "4" Then
  253.             result$ = EditMappings$
  254.         ElseIf in$ = "5" Then
  255.             result$ = ResetMapping$
  256.         ElseIf in$ = "6" Then
  257.             result$ = MapInput$
  258.         ElseIf in$ = "7" Then
  259.             result$ = TestMappings$
  260.         ElseIf in$ = "8" Then
  261.             result$ = SaveMappings$
  262.         End If
  263.        
  264.         If Len(result$) > 0 Then
  265.             Print result$
  266.         End If
  267.        
  268.     Loop Until in$ = "q"
  269.    
  270.     ' RETURN TO TEXT SCREEN
  271.     Screen 0
  272.    
  273. End Sub ' main
  274.  
  275. ' /////////////////////////////////////////////////////////////////////////////
  276. ' TODO: get keyboard input working
  277. ' TODO: get continuous movement working for digital joysticks
  278. ' TODO: adjust analog joystick sensitivity
  279.  
  280. Function TestMappings$
  281.     DIM iDeviceCount AS INTEGER
  282.     DIM iDevice AS INTEGER
  283.     DIM iNumControllers AS INTEGER
  284.     DIM iController AS INTEGER
  285.     Dim iValue As Integer
  286.     Dim iWhichInput As Integer
  287.    
  288.     DIM arrButton(32, 16) As Integer ' number of buttons on the joystick
  289.     DIM arrButtonNew(32, 16) As Integer ' tracks when to initialize values
  290.     DIM arrAxis(32, 16) As Double ' number of axis on the joystick
  291.     DIM arrAxisNew(32, 16) As Integer ' tracks when to initialize values
  292.    
  293.     Dim iCols As Integer
  294.     Dim iRows As Integer
  295.    
  296.     Dim iPlayer As Integer
  297.     Dim iNextY As Integer
  298.     Dim iNextX As Integer
  299.     Dim iNextC As Integer
  300.    
  301.     Dim iMinX As Integer
  302.     Dim iMaxX As Integer
  303.     Dim iMinY As Integer
  304.     Dim iMaxY As Integer
  305.    
  306.     Dim sError As String : sError = ""
  307.     Dim bHaveInput As Integer
  308.     Dim bFinished As Integer
  309.     Dim bFoundWho As Integer
  310.    
  311.     Dim in$
  312.    
  313.     ' INITIALIZE
  314.     CLS
  315.     iCols = _WIDTH(0) \ _FontWidth
  316.     iRows = _HEIGHT(0) \ _FontHeight
  317.     iMinX = 0 : iMaxX = iCols
  318.     iMinY = 0 : iMaxY = iRows
  319.    
  320.     ' INITIALIZE PLAYER COORDINATES AND SCREEN CHARACTERS
  321.     iNextY = 1
  322.     iNextX = -3
  323.     iNextC = 64
  324.     FOR iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  325.         iNextX = iNextX + 4
  326.         IF iNextX >= iMaxX THEN
  327.             iNextX = iMinX
  328.             iNextY = iNextY + 4
  329.             if iNextY > iMaxY THEN
  330.                 iNextY = iMinY
  331.             end if
  332.         END IF
  333.         iNextC = iNextC + 1
  334.         m_arrPlayer(iPlayer).x = iNextX
  335.         m_arrPlayer(iPlayer).y = iNextY
  336.         m_arrPlayer(iPlayer).c = iNextC
  337.         m_arrPlayer(iPlayer).xOld = iNextX
  338.         m_arrPlayer(iPlayer).yOld = iNextY
  339.        
  340.         m_arrPlayer(iPlayer).moveX = 0
  341.         m_arrPlayer(iPlayer).lastMoveX = 0
  342.         m_arrPlayer(iPlayer).moveY = 0
  343.         m_arrPlayer(iPlayer).lastMoveY = 0
  344.         m_arrPlayer(iPlayer).button1 = 0
  345.         m_arrPlayer(iPlayer).lastButton1 = 0
  346.         m_arrPlayer(iPlayer).button2 = 0
  347.         m_arrPlayer(iPlayer).lastButton2 = 0
  348.         m_arrPlayer(iPlayer).button3 = 0
  349.         m_arrPlayer(iPlayer).lastButton3 = 0
  350.         m_arrPlayer(iPlayer).button4 = 0
  351.         m_arrPlayer(iPlayer).lastButton4 = 0
  352.     NEXT iPlayer
  353.    
  354.     ' COUNT # OF JOYSTICKS
  355.     ' TODO: find out the right way to count joysticks
  356.     if len(sError) = 0 then
  357.         ' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
  358.         iDeviceCount = _DEVICES ' Find the number of devices on someone's system
  359.        
  360.         IF iDeviceCount > 2 THEN
  361.             ' LIMIT # OF DEVICES, IF THERE IS A LIMIT DEFINED
  362.             iNumControllers = iDeviceCount - 2
  363.             IF cMaxControllers > 0 THEN
  364.                 IF iNumControllers > cMaxControllers THEN
  365.                     iNumControllers = cMaxControllers
  366.                 END IF
  367.             END IF
  368.         ELSE
  369.             ' ONLY 2 FOUND (KEYBOARD, MOUSE)
  370.             'sError = "No game controllers found."
  371.             iNumControllers = 0
  372.         END IF
  373.     end if
  374.    
  375.     ' INITIALIZE CONTROLLER DATA
  376.     if len(sError) = 0 then
  377.         FOR iController = 1 TO iNumControllers
  378.             m_arrController(iController).buttonCount = cMaxButtons
  379.             m_arrController(iController).axisCount = cMaxAxis
  380.             for iLoop = 1 to cMaxButtons
  381.                 arrButtonNew(iController, iLoop) = TRUE
  382.             next iloop
  383.             for iLoop = 1 to cMaxAxis
  384.                 arrAxisNew(iController, iLoop) = TRUE
  385.             next iLoop
  386.         NEXT iController
  387.     end if
  388.    
  389.     ' INITIALIZE CONTROLLER INPUT
  390.     if len(sError) = 0 then
  391.         _KEYCLEAR: _DELAY 1
  392.         FOR iController = 1 TO iNumControllers
  393.             iDevice = iController + 2
  394.             WHILE _DEVICEINPUT(iDevice) ' clear and update the device buffer
  395.                 FOR iLoop = 1 TO _LASTBUTTON(iDevice)
  396.                     IF (iLoop > cMaxButtons) THEN EXIT FOR
  397.                     m_arrController(iController).buttonCount = iLoop
  398.                     arrButton(iController, iLoop) = FALSE
  399.                 NEXT iLoop
  400.                 FOR iLoop = 1 TO _LASTAXIS(iDevice) ' this loop checks all my axis
  401.                     IF (iLoop > cMaxAxis) THEN EXIT FOR
  402.                     m_arrController(iController).axisCount = iLoop
  403.                     arrAxis(iController, iLoop) = 0
  404.                 NEXT iLoop
  405.             WEND ' clear and update the device buffer
  406.         NEXT iController
  407.     end if
  408.    
  409.     ' GET INPUT AND MOVE PLAYERS AROUND ON SCREEN
  410.     _KEYCLEAR: _DELAY 1
  411.     bFinished = FALSE
  412.     Do
  413.         ' Clear control buffer for players
  414.         FOR iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  415.             m_arrPlayer(iPlayer).lastMoveX = m_arrPlayer(iPlayer).moveX
  416.             m_arrPlayer(iPlayer).moveX = 0
  417.             m_arrPlayer(iPlayer).lastMoveY = m_arrPlayer(iPlayer).moveY
  418.             m_arrPlayer(iPlayer).moveY = 0
  419.             m_arrPlayer(iPlayer).lastButton1 = m_arrPlayer(iPlayer).button1
  420.             m_arrPlayer(iPlayer).button1 = 0
  421.             m_arrPlayer(iPlayer).lastButton2 = m_arrPlayer(iPlayer).button2
  422.             m_arrPlayer(iPlayer).button2 = 0
  423.             m_arrPlayer(iPlayer).lastButton3 = m_arrPlayer(iPlayer).button3
  424.             m_arrPlayer(iPlayer).button3 = 0
  425.             m_arrPlayer(iPlayer).lastButton4 = m_arrPlayer(iPlayer).button4
  426.             m_arrPlayer(iPlayer).button4 = 0
  427.         NEXT iPlayer
  428.        
  429.         ' -----------------------------------------------------------------------------
  430.         ' BEGIN CHECK FOR CONTROLLER INPUT
  431.         if iNumControllers > 0 then
  432.             FOR iController = 1 TO iNumControllers
  433.                 iDevice = iController + 2
  434.                
  435.                 ' Check all devices
  436.                 WHILE _DEVICEINPUT(iDevice)
  437.                    
  438.                     ' Check each button
  439.                     FOR iLoop = 1 TO _LASTBUTTON(iDevice)
  440.                         IF (iLoop > cMaxButtons) THEN EXIT FOR
  441.                        
  442.                         ' update button array to indicate if a button is up or down currently.
  443.                         if TRUE=TRUE then
  444.                         'IF _BUTTONCHANGE(iLoop) THEN
  445.                             iValue = _BUTTON(iLoop)
  446.                             if iValue <> arrButton(iController, iLoop) then
  447.                                 ' *****************************************************************************
  448.                                 ' PRESSED BUTTON
  449.                                
  450.                                 ' BEGIN find who this is mapped for
  451.                                 bFoundWho = FALSE
  452.                                 For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  453.                                     For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  454.                                         if m_arrControlMap(iPlayer, iWhichInput).device = iDevice then
  455.                                             if m_arrControlMap(iPlayer, iWhichInput).typ = cInputButton then
  456.                                                 if m_arrControlMap(iPlayer, iWhichInput).code = iLoop then
  457.                                                     'if m_arrControlMap(iPlayer, iWhichInput).value = iValue then
  458.                                                         bFoundWho = TRUE
  459.                                                         Select Case iWhichInput
  460.                                                             Case cInputUp:
  461.                                                                 m_arrPlayer(iPlayer).moveY = -1
  462.                                                             Case cInputDown:
  463.                                                                 m_arrPlayer(iPlayer).moveY = 1
  464.                                                             Case cInputLeft:
  465.                                                                 m_arrPlayer(iPlayer).moveX = -1
  466.                                                             Case cInputRight:
  467.                                                                 m_arrPlayer(iPlayer).moveX = 1
  468.                                                             Case cInputButton1:
  469.                                                                 m_arrPlayer(iPlayer).button1 = TRUE
  470.                                                             Case cInputButton2:
  471.                                                                 m_arrPlayer(iPlayer).button2 = TRUE
  472.                                                             Case cInputButton3:
  473.                                                                 m_arrPlayer(iPlayer).button3 = TRUE
  474.                                                             Case cInputButton4:
  475.                                                                 m_arrPlayer(iPlayer).button4 = TRUE
  476.                                                             Case Else:
  477.                                                                 '(IGNORE)
  478.                                                         End Select
  479.                                                         Exit For
  480.                                                     'end if
  481.                                                 end if
  482.                                             end if
  483.                                         end if
  484.                                     Next iWhichInput
  485.                                     If bFoundWho=TRUE Then Exit For
  486.                                 Next iPlayer
  487.                                 ' END find who this is mapped for
  488.                                
  489.                             end if
  490.                         END IF
  491.                     NEXT iLoop
  492.                    
  493.                     ' Check each axis
  494.                     FOR iLoop = 1 TO _LASTAXIS(iDevice)
  495.                         IF (iLoop > cMaxAxis) THEN EXIT FOR
  496.                         dblNextAxis = _AXIS(iLoop)
  497.                         dblNextAxis = RoundUpDouble# (dblNextAxis, 3)
  498.                        
  499.                         ' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
  500.                         ' which is prone to always give minute values and never really center on true 0.
  501.                         ' A value of 1 means my axis is pushed fully in one direction.
  502.                         ' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
  503.                         ' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
  504.                        
  505.                         ' Set sensitivity:
  506.                         'These are way too sensitive for analog:
  507.                         'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
  508.                         'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
  509.                         'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .001 THEN
  510.                         ''For digital input, we'll use a big picture:
  511.                         'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= 0.75 THEN
  512.                         IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= 0.5 THEN
  513.                        
  514.                             ' WE WANT CONTINUOUS MOVEMENT (DISABLE FOR NOT)
  515.                             if TRUE=TRUE then
  516.                             'if dblNextAxis <> arrAxis(iController, iLoop) then
  517.                                 ' *****************************************************************************
  518.                                 ' MOVED STICK
  519.                                
  520.                                 ' convert to a digital value
  521.                                 if dblNextAxis < 0 then
  522.                                     iValue = -1
  523.                                 else
  524.                                     iValue = 1
  525.                                 end if
  526.                                
  527.                                 ' BEGIN find who this is mapped for
  528.                                 bFoundWho = FALSE
  529.                                 For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  530.                                     For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  531.                                         if m_arrControlMap(iPlayer, iWhichInput).device = iDevice then
  532.                                             if m_arrControlMap(iPlayer, iWhichInput).typ = cInputAxis then
  533.                                                 if m_arrControlMap(iPlayer, iWhichInput).code = iLoop then
  534.                                                     if m_arrControlMap(iPlayer, iWhichInput).value = iValue then
  535.                                                         bFoundWho = TRUE
  536.                                                         Select Case iWhichInput
  537.                                                             Case cInputUp:
  538.                                                                 m_arrPlayer(iPlayer).moveY = -1
  539.                                                             Case cInputDown:
  540.                                                                 m_arrPlayer(iPlayer).moveY = 1
  541.                                                             Case cInputLeft:
  542.                                                                 m_arrPlayer(iPlayer).moveX = -1
  543.                                                             Case cInputRight:
  544.                                                                 m_arrPlayer(iPlayer).moveX = 1
  545.                                                             Case cInputButton1:
  546.                                                                 m_arrPlayer(iPlayer).button1 = TRUE
  547.                                                             Case cInputButton2:
  548.                                                                 m_arrPlayer(iPlayer).button2 = TRUE
  549.                                                             Case cInputButton3:
  550.                                                                 m_arrPlayer(iPlayer).button3 = TRUE
  551.                                                             Case cInputButton4:
  552.                                                                 m_arrPlayer(iPlayer).button4 = TRUE
  553.                                                             Case Else:
  554.                                                                 '(IGNORE)
  555.                                                         End Select
  556.                                                         Exit For
  557.                                                     end if
  558.                                                 end if
  559.                                             end if
  560.                                         end if
  561.                                     Next iWhichInput
  562.                                     If bFoundWho=TRUE Then Exit For
  563.                                 Next iPlayer
  564.                                 ' END find who this is mapped for
  565.                                
  566.                             end if
  567.                         END IF
  568.                     NEXT iLoop
  569.                    
  570.                 WEND ' clear and update the device buffer
  571.                
  572.             NEXT iController
  573.         End If
  574.         ' END CHECK FOR CONTROLLER INPUT
  575.         ' -----------------------------------------------------------------------------
  576.        
  577.         ' -----------------------------------------------------------------------------
  578.         ' BEGIN CHECK FOR KEYBOARD INPUT #1
  579.         '_KEYCLEAR: _DELAY 1
  580.         WHILE _DEVICEINPUT(1): WEND ' clear and update the keyboard buffer
  581.        
  582.         ' Detect changed key state
  583.         For iLoop = lbound(m_arrButtonCode) to ubound(m_arrButtonCode)
  584.             iCode = m_arrButtonCode(iLoop)
  585.             If _BUTTON(iCode) <> FALSE Then
  586.                 ' *****************************************************************************
  587.                 ' PRESSED KEYBOARD
  588.                 'PRINT "PRESSED " + m_arrButtonKey(iLoop)
  589.                
  590.                 ' BEGIN find who this is mapped for
  591.                 bFoundWho = FALSE
  592.                 For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  593.                     For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  594.                         if m_arrControlMap(iPlayer, iWhichInput).device = iDevice then
  595.                             if m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey then
  596.                                
  597.                                 'if m_arrControlMap(iPlayer, iWhichInput).code = iLoop then
  598.                                 if m_arrControlMap(iPlayer, iWhichInput).code = iCode then
  599.                                     'if m_arrControlMap(iPlayer, iWhichInput).value = iValue then
  600.                                         bFoundWho = TRUE
  601.                                         Select Case iWhichInput
  602.                                             Case cInputUp:
  603.                                                 m_arrPlayer(iPlayer).moveY = -1
  604.                                             Case cInputDown:
  605.                                                 m_arrPlayer(iPlayer).moveY = 1
  606.                                             Case cInputLeft:
  607.                                                 m_arrPlayer(iPlayer).moveX = -1
  608.                                             Case cInputRight:
  609.                                                 m_arrPlayer(iPlayer).moveX = 1
  610.                                             Case cInputButton1:
  611.                                                 m_arrPlayer(iPlayer).button1 = TRUE
  612.                                             Case cInputButton2:
  613.                                                 m_arrPlayer(iPlayer).button2 = TRUE
  614.                                             Case cInputButton3:
  615.                                                 m_arrPlayer(iPlayer).button3 = TRUE
  616.                                             Case cInputButton4:
  617.                                                 m_arrPlayer(iPlayer).button4 = TRUE
  618.                                             Case Else:
  619.                                                 '(IGNORE)
  620.                                         End Select
  621.                                         Exit For
  622.                                     'end if
  623.                                 end if
  624.                             end if
  625.                         end if
  626.                     Next iWhichInput
  627.                     If bFoundWho=TRUE Then Exit For
  628.                 Next iPlayer
  629.                 ' END find who this is mapped for
  630.                
  631.             End If
  632.         Next iLoop
  633.         ' END CHECK FOR KEYBOARD INPUT #1
  634.         ' -----------------------------------------------------------------------------
  635.        
  636.        
  637.        
  638.         ' NOW DRAW PLAYERS ON SCREEN
  639.         FOR iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  640.            
  641.             ' MOVE RIGHT/LEFT
  642.             m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).x + m_arrPlayer(iPlayer).moveX
  643.             if m_arrPlayer(iPlayer).x < iMinX then
  644.                 m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).xOld ' iMinX
  645.             elseif m_arrPlayer(iPlayer).x > iMaxX then
  646.                 m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).xOld ' iMaxX
  647.             end if
  648.            
  649.             ' MOVE UP/DOWN
  650.             m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).y + m_arrPlayer(iPlayer).moveY
  651.             if m_arrPlayer(iPlayer).y < iMinY then
  652.                 m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).yOld ' iMinY
  653.             elseif m_arrPlayer(iPlayer).y > iMaxY then
  654.                 m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).yOld ' iMaxY
  655.             end if
  656.            
  657.             ' UPDATE SCREEN
  658.             '_PRINTSTRING (m_arrPlayer(iPlayer).xOld, m_arrPlayer(iPlayer).yOld), " "
  659.             '_PRINTSTRING (m_arrPlayer(iPlayer).x, m_arrPlayer(iPlayer).y), CHR$(m_arrPlayer(iPlayer).c)
  660.             PrintString m_arrPlayer(iPlayer).xOld, m_arrPlayer(iPlayer).yOld, " "
  661.             PrintString m_arrPlayer(iPlayer).x, m_arrPlayer(iPlayer).y, CHR$(m_arrPlayer(iPlayer).c)
  662.             m_arrPlayer(iPlayer).xOld = m_arrPlayer(iPlayer).x
  663.             m_arrPlayer(iPlayer).yOld = m_arrPlayer(iPlayer).y
  664.            
  665.             ' BUTTON ACTIONS
  666.             if m_arrPlayer(iPlayer).button1 = TRUE then
  667.                 MakeSound iPlayer, 1
  668.             end if
  669.            
  670.             if m_arrPlayer(iPlayer).button2 = TRUE then
  671.                 MakeSound iPlayer, 2
  672.             end if
  673.            
  674.             if m_arrPlayer(iPlayer).button3 = TRUE then
  675.                 MakeSound iPlayer, 3
  676.             end if
  677.            
  678.             if m_arrPlayer(iPlayer).button4 = TRUE then
  679.                 MakeSound iPlayer, 4
  680.             end if
  681.         NEXT iPlayer
  682.        
  683.         _LIMIT 30
  684.     LOOP UNTIL _KEYHIT = 27 ' ESCAPE to quit
  685.    
  686.     _KEYCLEAR: _DELAY 1
  687.    
  688. End Function ' TestMappings$
  689.  
  690. ' /////////////////////////////////////////////////////////////////////////////
  691.  
  692. Sub MakeSound(iPlayer As Integer, iButton As Integer)
  693.     Dim note%
  694.     If iPlayer < 1 Then
  695.         iPlayer = 1
  696.     ElseIf iPlayer > 8 Then
  697.         iPlayer = 8
  698.     End If
  699.     If iButton < 1 Then
  700.         iButton = 1
  701.     ElseIf iButton > 4 Then
  702.         iButton = 4
  703.     End If
  704.    
  705.     note% = iPlayer * 100 + (iButton * 25)
  706.     IF note% > 4186 THEN
  707.         note% = 4186
  708.     END IF
  709.     SOUND note%, .75
  710. End Sub ' MakeSound
  711.  
  712. ' /////////////////////////////////////////////////////////////////////////////
  713. ' V2 prints in 2 columns.
  714.  
  715. Sub PrintControllerMap2
  716.     Dim RoutineName As String:: RoutineName = "PrintControllerMap2"
  717.     Dim iPlayer As Integer
  718.     Dim iWhichInput As Integer
  719.     Dim iCount As Integer
  720.     Dim sLine As String
  721.     Dim iHalf As Integer
  722.     Dim sColumn1 As String : sColumn1 = ""
  723.     Dim sColumn2 As String : sColumn2 = ""
  724.     ReDim arrColumn1(-1) As String
  725.     ReDim arrColumn2(-1) As String
  726.     Dim iLoop As Integer
  727.     Dim iColWidth As Integer : iColWidth = 65
  728.     Dim sValue As String
  729.     Dim in$
  730.    
  731.     ' INITIALIZE
  732.     InitKeyboardButtonCodes
  733.    
  734.     ' START OUTPUT
  735.     Print "Controller mapping:"
  736.     'Print "Player#  Input      Device#  Type     Code              Value"
  737.     '       1        button #2  x        unknown  x                 x
  738.     '       9        11         9        9        18                9
  739.     '       12345678912345678901123456789123456789123456789012345678123456789
  740.     '       12345678901234567890123456789012345678901234567890123456789012345678901234567890
  741.     '       00000000011111111112222222222333333333344444444445555555555666666666677777777778
  742.    
  743.     ' THIS IS A LAZY WAY TO GET 2 COLUMNS!
  744.     iHalf = UBound(m_arrControlMap, 1) / 2
  745.    
  746.     sLine = "Player#  Input      Device#  Type     Code              Value"
  747.     sColumn1 = sColumn1 + StrPadRight$(sLine, iColWidth) + chr$(13)
  748.     sLine = "-------------------------------------------------------------"
  749.     sColumn1 = sColumn1 + StrPadRight$(sLine, iColWidth) + chr$(13)
  750.     For iPlayer = LBound(m_arrControlMap, 1) To iHalf
  751.         iCount = 0
  752.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  753.             if InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" then
  754.                 iCount = iCount + 1
  755.             end if
  756.         Next iWhichInput
  757.         if iCount > 0 then
  758.             For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  759.                 if InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" then
  760.                     sLine = IntPadRight$(iPlayer, 9)
  761.                     sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
  762.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
  763.                     sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
  764.                    
  765.                     'sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
  766.                     if m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey then
  767.                         sValue = GetKeyboardButtonCodeText$( m_arrControlMap(iPlayer, iWhichInput).code )
  768.                         sValue = StrPadRight$(sValue, 18)
  769.                     else
  770.                         sValue = IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 18)
  771.                     end if
  772.                     sLine = sLine + sValue
  773.                    
  774.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
  775.                     'Print sLine
  776.                     sLine = StrPadRight$(sLine, iColWidth)
  777.                     sColumn1 = sColumn1 + sLine + chr$(13)
  778.                 end if
  779.             Next iWhichInput
  780.         else
  781.             sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
  782.             'Print sLine
  783.             sLine = StrPadRight$(sLine, iColWidth)
  784.             sColumn1 = sColumn1 + sLine + chr$(13)
  785.         end if
  786.     Next iPlayer
  787.    
  788.     sLine = "Player#  Input      Device#  Type     Code              Value"
  789.     sColumn2 = sColumn2 + StrPadRight$(sLine, iColWidth) + chr$(13)
  790.     sLine = "-------------------------------------------------------------"
  791.     sColumn2 = sColumn2 + StrPadRight$(sLine, iColWidth) + chr$(13)
  792.     For iPlayer = iHalf + 1 To UBound(m_arrControlMap, 1)
  793.         iCount = 0
  794.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  795.             if InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" then
  796.                 iCount = iCount + 1
  797.             end if
  798.         Next iWhichInput
  799.         if iCount > 0 then
  800.             For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  801.                 if InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" then
  802.                     sLine = IntPadRight$(iPlayer, 9)
  803.                     sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
  804.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
  805.                     sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
  806.                    
  807.                     'sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
  808.                     if m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey then
  809.                         sValue = GetKeyboardButtonCodeText$( m_arrControlMap(iPlayer, iWhichInput).code )
  810.                         sValue = StrPadRight$(sValue, 18)
  811.                     else
  812.                         sValue = IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 18)
  813.                     end if
  814.                     sLine = sLine + sValue
  815.                    
  816.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
  817.                     'Print sLine
  818.                     sLine = StrPadRight$(sLine, iColWidth)
  819.                     sColumn2 = sColumn2 + sLine + chr$(13)
  820.                 end if
  821.             Next iWhichInput
  822.         else
  823.             sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
  824.             'Print sLine
  825.             sLine = StrPadRight$(sLine, iColWidth)
  826.             sColumn2 = sColumn2 + sLine + chr$(13)
  827.         end if
  828.     Next iPlayer
  829.    
  830.     split sColumn1, chr$(13), arrColumn1()    
  831.     split sColumn2, chr$(13), arrColumn2()    
  832.     if ubound(arrColumn1) > ubound(arrColumn2) then
  833.         iCount = ubound(arrColumn1)
  834.     else
  835.         iCount = ubound(arrColumn2)
  836.     end if
  837.     for iLoop = 0 to iCount
  838.         sLine = ""
  839.         if ubound(arrColumn1) >= iLoop then
  840.             sLine = sLine + arrColumn1(iLoop)
  841.         else
  842.             sLine = sLine + STRING$(iColWidth, " ")
  843.         end if
  844.         sLine = sLine + "     "
  845.         if ubound(arrColumn2) >= iLoop then
  846.             sLine = sLine + arrColumn2(iLoop)
  847.         else
  848.             sLine = sLine + STRING$(iColWidth, " ")
  849.         end if
  850.         print sLine
  851.     next iLoop
  852. End Sub ' PrintControllerMap2
  853.  
  854. ' /////////////////////////////////////////////////////////////////////////////
  855. ' Original (simple) routine
  856.  
  857. Sub PrintControllerMap1
  858.     Dim RoutineName As String:: RoutineName = "PrintControllerMap1"
  859.     Dim iPlayer As Integer
  860.     Dim iWhichInput As Integer
  861.     Dim sLine As String
  862.     Dim iCount As Integer
  863.     Dim in$
  864.    
  865.     ' INITIALIZE
  866.     InitKeyboardButtonCodes
  867.    
  868.     ' OUTPUT MAPPING
  869.     Print "Controller mapping:"
  870.     Print "Player#  Input      Device#  Type     Code     Value"
  871.     '      1        button #2  x        unknown  x        x
  872.     '      9        11         9        9        9        9
  873.     '      12345678912345678901123456789123456789123456789123456789
  874.     '      12345678901234567890123456789012345678901234567890123456789012345678901234567890
  875.     For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  876.         iCount = 0
  877.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  878.             if InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" then
  879.                 iCount = iCount + 1
  880.             end if
  881.         Next iWhichInput
  882.         if iCount > 0 then
  883.             For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  884.                 if InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" then
  885.                     sLine = IntPadRight$(iPlayer, 9)
  886.                     sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
  887.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
  888.                     sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
  889.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
  890.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
  891.                     Print sLine
  892.                 end if
  893.             Next iWhichInput
  894.         else
  895.             sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
  896.             Print sLine
  897.         end if
  898.     Next iPlayer
  899. End Sub ' PrintControllerMap1
  900.  
  901. ' /////////////////////////////////////////////////////////////////////////////
  902.  
  903. Function LoadMappings$
  904.     Dim sResult As String : sResult = ""
  905.    
  906.     ' INITIALIZE
  907.     InitKeyboardButtonCodes
  908.    
  909.     ' Try loading map
  910.     sResult = LoadControllerMap$
  911.    
  912.     LoadMappings$ = sResult
  913. End Function ' LoadMappings$
  914.  
  915. ' /////////////////////////////////////////////////////////////////////////////
  916.  
  917. Function SaveMappings$
  918.     Dim sResult As String : sResult = ""
  919.     Dim sError As String : sError = ""
  920.    
  921.     ' INITIALIZE
  922.     InitKeyboardButtonCodes
  923.    
  924.     ' Try saving map
  925.     sResult = SaveControllerMap$
  926.    
  927.     SaveMappings$ = sResult
  928. End Function ' SaveMappings$
  929.  
  930.  
  931. ' /////////////////////////////////////////////////////////////////////////////
  932.  
  933. Function ViewMappings$
  934.     ' INITIALIZE
  935.     InitKeyboardButtonCodes
  936.    
  937.     PrintControllerMap2
  938.     Print
  939.     Input "PRESS <ENTER> TO CONTINUE", in$
  940.     Print
  941.     ViewMappings$ = ""
  942. End Function ' ViewMappings$
  943.  
  944. ' /////////////////////////////////////////////////////////////////////////////
  945. ' TODO: test this
  946.  
  947. Function EditMappings$
  948.     Dim RoutineName As String: RoutineName = "EditMappings$"
  949.     Dim in$
  950.     Dim iPlayer As Integer
  951.     Dim iWhichInput As Integer
  952.     Dim iDevice As Integer
  953.     Dim iType As Integer
  954.     Dim iWhich As Integer
  955.     Dim iValue As Integer
  956.     Dim iItem As Integer
  957.     Dim sResult As String : sResult = ""
  958.     Dim bContinue1 As Integer : bContinue1 = TRUE
  959.     Dim bContinue2 As Integer : bContinue2 = TRUE
  960.     Dim bContinue3 As Integer : bContinue3 = TRUE
  961.     Dim bContinue4 As Integer : bContinue4 = TRUE
  962.    
  963.     ' INITIALIZE
  964.     InitKeyboardButtonCodes
  965.    
  966.     ' EDIT
  967.     Do
  968.         PrintControllerMap2
  969.         Print "To edit a mapping, enter a player number: " _
  970.             "1-" + cstr$(cMaxPlayers) + ", " + _
  971.             cstr$(cMaxPlayers+1) + ") or 0 to exit."
  972.         Input "Edit mapping for player? ";iPlayer
  973.         if iPlayer > 0 and iPlayer <= cMaxPlayers then
  974.             bContinue2 = TRUE
  975.             Do
  976.                 Print "Editing mappings for player " + cstr$(iPlayer) + "."
  977.                 For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  978.                     Print right$("  " + cstr$(iWhichInput), 2) + ". " + InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ)
  979.                 Next iWhichInput
  980.                 Input "Type # of control to edit or q to quit editing player.";in$
  981.                 if IsNum%(in$) then
  982.                     iWhichInput = VAL(in$)
  983.                     if iWhichInput >= lbound(m_arrControlMap, 2) and m_arrControlMap <= ubound(m_arrControlMap, 2) then
  984.                         bContinue3 = TRUE
  985.                         Do
  986.                             Print "Settings for " + chr$(34) + InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) + chr$(34) + ":"
  987.                             Print "1. Device #   : " + cstr$(m_arrControlMap(iPlayer, iWhichInput).device)
  988.                             Print "2. Device type: " + InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ)
  989.                             Print "3. Input code : " + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).code))
  990.                             Print "4. Input value: " + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).value))
  991.                             INPUT "Change item? (1-4 or q to quit editing control)";in$
  992.                             if IsNum%(in$) then
  993.                                 iItem = VAL(in$)
  994.                                 Select Case iItem
  995.                                     Case 1:
  996.                                         Print "Change the device number."
  997.                                         Input "Type a new device #, 0 for none, or blank to leave it unchanged";in$
  998.                                         if IsNum%(in$) then
  999.                                             iDevice = VAL(in$)
  1000.                                             m_arrControlMap(iPlayer, iWhichInput).device = iDevice
  1001.                                             Print "Updated device number. Remember to save mappings when done."
  1002.                                         else
  1003.                                             Print "(No change.)"
  1004.                                         end if
  1005.                                     Case 2:
  1006.                                         bContinue4 = TRUE
  1007.                                         Do
  1008.                                             Print "Change the device type."
  1009.                                             Print cstr$(cInputKey) + "=keyboard"
  1010.                                             Print cstr$(cInputButton) + "=game controller button"
  1011.                                             Print cstr$(cInputAxis) + "=game controller joystick/axis"
  1012.                                             Print cstr$(cInputNone) + "=none"
  1013.                                             Input "Device type or blank to leave it unchanged";in$
  1014.                                             if IsNum%(in$) then
  1015.                                                 iType = VAL(in$)
  1016.                                                 if iType=cInputKey or iType=cInputButton or _
  1017.                                                     iType=cInputAxis or iType=cInputNone then
  1018.                                                    
  1019.                                                     m_arrControlMap(iPlayer, iWhichInput).typ = iType
  1020.                                                     Print "Updated device type. Remember to save mappings when done."
  1021.                                                     bContinue4 = FALSE : Exit Do
  1022.                                                 else
  1023.                                                     Print "Please choose one of the listed values."
  1024.                                                 end if
  1025.                                             else
  1026.                                                 Print "(No change.)"
  1027.                                                 bContinue4 = FALSE : Exit Do
  1028.                                             end if
  1029.                                         Loop Until bContinue4 = FALSE
  1030.                                     Case 3:
  1031.                                         Print "Change the input code."
  1032.                                         Input "Type a new input code, or blank to leave it unchanged";in$
  1033.                                         if IsNum%(in$) then
  1034.                                             iWhich = VAL(in$)
  1035.                                             m_arrControlMap(iPlayer, iWhichInput).code = iWhich
  1036.                                             Print "Updated input code. Remember to save mappings when done."
  1037.                                         else
  1038.                                             Print "(No change.)"
  1039.                                         end if
  1040.                                     Case 4:
  1041.                                         Print "Change the input value."
  1042.                                         Input "Type a new input value, or blank to leave it unchanged";in$
  1043.                                         if IsNum%(in$) then
  1044.                                             iValue = VAL(in$)
  1045.                                             m_arrControlMap(iPlayer, iWhichInput).value = value
  1046.                                             Print "Updated input value. Remember to save mappings when done."
  1047.                                         else
  1048.                                             Print "(No change.)"
  1049.                                         end if
  1050.                                     Case Else:
  1051.                                         Print "Please choose a number between 1 and 4."
  1052.                                 End Select
  1053.                             else
  1054.                                 bContinue3 = FALSE : Exit Do
  1055.                             end if
  1056.                         Loop Until bContinue3 = FALSE
  1057.                     else
  1058.                         Print "Please choose a number between " + cstr$(lbound(m_arrControlMap, 2)) + " and " + cstr$(ubound(m_arrControlMap, 2)) + "."
  1059.                     end if
  1060.                 else
  1061.                     bContinue2 = FALSE : Exit Do
  1062.                 end if
  1063.             Loop Until bContinue2 = FALSE
  1064.             if bContinue1 = FALSE then exit do
  1065.         else
  1066.             if len(sResult) = 0 then sResult = "(Cancelled.)"
  1067.             bContinue1 = FALSE : Exit Do
  1068.         end if
  1069.     Loop Until bContinue1 = FALSE
  1070.    
  1071.     _KEYCLEAR: _DELAY 1
  1072.    
  1073.     EditMappings$ = sResult
  1074. END Function ' EditMappings$
  1075.  
  1076. ' /////////////////////////////////////////////////////////////////////////////
  1077.  
  1078. Function ResetMapping$
  1079.     Dim RoutineName As String: RoutineName = "ResetMapping$"
  1080.     Dim in$
  1081.     Dim iPlayer As Integer
  1082.     Dim sResult As String : sResult = ""
  1083.    
  1084.     ' INITIALIZE
  1085.     InitKeyboardButtonCodes
  1086.    
  1087.     ' RESET
  1088.     Do
  1089.         PrintControllerMap2
  1090.        
  1091.         Print "To delete mapping, enter a player number: " _
  1092.             "1-" + cstr$(cMaxPlayers) + ", " + _
  1093.             cstr$(cMaxPlayers+1) + " for all, or 0 to exit."
  1094.         Input "Delete mapping for player? ";iPlayer
  1095.        
  1096.         if iPlayer > 0 and iPlayer <= cMaxPlayers then
  1097.             Print "Delete mappings for player " + cstr$(iPlayer) + "."
  1098.             Input "Delete (y/n)";in$ : in$ = lcase$(_Trim$(in$))
  1099.             if in$ = "y" then
  1100.                 For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  1101.                     if InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" then
  1102.                         m_arrControlMap(iPlayer, iWhichInput).device = 0
  1103.                         m_arrControlMap(iPlayer, iWhichInput).typ = 0
  1104.                         m_arrControlMap(iPlayer, iWhichInput).code = 0
  1105.                         m_arrControlMap(iPlayer, iWhichInput).value = 0
  1106.                     end if
  1107.                 Next iWhichInput
  1108.                 sResult = "Mappings deleted for player " + cstr$(iPlayer) + "."
  1109.                 print sResult
  1110.             end if
  1111.         elseif iPlayer = (cMaxPlayers+1) then
  1112.             Input "Delete all mappings (y/n)";in$ : in$ = lcase$(_Trim$(in$))
  1113.             if in$ = "y" then
  1114.                 For iPlayer = 1 to cMaxPlayers
  1115.                     For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  1116.                         if InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" then
  1117.                             m_arrControlMap(iPlayer, iWhichInput).device = 0
  1118.                             m_arrControlMap(iPlayer, iWhichInput).typ = 0
  1119.                             m_arrControlMap(iPlayer, iWhichInput).code = 0
  1120.                             m_arrControlMap(iPlayer, iWhichInput).value = 0
  1121.                         end if
  1122.                     Next iWhichInput
  1123.                 Next iPlayer
  1124.                 sResult = "All mappings deleted."
  1125.                 print sResult
  1126.             end if
  1127.         else
  1128.             if len(sResult) = 0 then sResult = "(Cancelled.)"
  1129.             exit do
  1130.         end if
  1131.     Loop
  1132.     ResetMapping$ = sResult
  1133. END Function ' ResetMapping$
  1134.  
  1135. ' /////////////////////////////////////////////////////////////////////////////
  1136.  
  1137. Function MapInput$
  1138.     Dim RoutineName As String: RoutineName = "MapInput$"
  1139.     Dim in$
  1140.     DIM iDeviceCount As Integer
  1141.     Dim iPlayer As Integer
  1142.     Dim sResult As String
  1143.     Dim sError As String
  1144.    
  1145.     ' INITIALIZE
  1146.     InitKeyboardButtonCodes
  1147.    
  1148.     ' SET UP SCREEN
  1149.     Screen _NewImage(1280, 1024, 32): _ScreenMove 0, 0
  1150.    
  1151.     ' MAKE SURE WE HAVE DEVICES
  1152.     ' 1 is the keyboard
  1153.     ' 2 is the mouse
  1154.     ' 3 is the joystick
  1155.     ' unless someone has a strange setup with multiple mice/keyboards/ect...
  1156.     ' In that case, you can use _DEVICE$(i) to look for "KEYBOARD", "MOUSE", "JOYSTICK", if necessary.
  1157.     ' I've never actually found it necessary, but I figure it's worth mentioning, just in case...
  1158.     iDeviceCount = _DEVICES ' Find the number of devices on someone's system
  1159.     IF iDeviceCount > 2 THEN
  1160.         '' Try loading map
  1161.         'sError = LoadControllerMap$
  1162.         'if len(sError) = 0 then
  1163.         '    print "Previous controller mapping loaded."
  1164.         'else
  1165.         '    print "*******************************************************************************"
  1166.         '    print "There were errors loading the controller mapping file:"
  1167.         '    print sError
  1168.         '    print
  1169.         '    print "Try remapping - a new file will be created."
  1170.         '    print "*******************************************************************************"
  1171.         'end if
  1172.         Do
  1173.             PrintControllerMap2
  1174.             Print "To edit mapping, enter a player number (1-" + cstr$(cMaxPlayers) + ") or 0 to exit."
  1175.             Input "Get input for player? ";iPlayer
  1176.             if iPlayer > 0 and iPlayer <= cMaxPlayers then
  1177.                 sResult = MapInput1$(iPlayer)
  1178.                 if len(sResult) = 0 then
  1179.                     Print "Remember to save mappings when done."
  1180.                 else
  1181.                     Print sResult
  1182.                 end if
  1183.             else
  1184.                 sResult = "(Cancelled.)"
  1185.                 exit do
  1186.             end if
  1187.         Loop
  1188.     ELSE
  1189.         sResult = "No controller devices found."
  1190.         INPUT "PRESS <ENTER> TO CONTINUE", in$
  1191.     END IF
  1192.     MapInput$ = sResult
  1193.    
  1194. END Function ' MapInput$
  1195.  
  1196. ' /////////////////////////////////////////////////////////////////////////////
  1197. ' Detect controls
  1198. ' THIS VERSION SUPPORTS UPTO 8 JOYSTICKS, WITH UPTO 2 BUTTONS AND 2 AXES EACH
  1199. ' (THIS IS FOR ATARI 2600 JOYSTICKS)
  1200.  
  1201. ' The following shared arrays must be declared:
  1202. '     ReDim Shared m_arrButtonCode(1 To 99) As Long
  1203. '     ReDim Shared m_arrButtonKey(1 To 99) As String
  1204.  
  1205. Function MapInput1$ (iPlayer As Integer)
  1206.     DIM RoutineName AS STRING:: RoutineName = "MapInput1$"
  1207.     Dim sResult As String : sResult = ""
  1208.     Dim sError As String : sError = ""
  1209.    
  1210.     DIM in$
  1211.    
  1212.     DIM iDeviceCount AS INTEGER
  1213.     DIM iDevice AS INTEGER
  1214.     DIM iNumControllers AS INTEGER
  1215.     DIM iController AS INTEGER
  1216.    
  1217.     DIM iLoop AS INTEGER
  1218.     DIM strValue AS STRING
  1219.     Dim strAxis AS STRING
  1220.     DIM dblNextAxis
  1221.     Dim iCount As Long
  1222.     Dim iValue As Integer
  1223.     Dim iCode As Integer
  1224.    
  1225.     DIM arrButton(32, 16) As Integer ' number of buttons on the joystick
  1226.     DIM arrButtonNew(32, 16) As Integer ' tracks when to initialize values
  1227.     DIM arrAxis(32, 16) As Double ' number of axis on the joystick
  1228.     DIM arrAxisNew(32, 16) As Integer ' tracks when to initialize values
  1229.    
  1230.     'Dim arrInput(1 To 8) As ControlInputType
  1231.     Dim iWhichInput As Integer
  1232.     Dim bFinished As Integer
  1233.     Dim bHaveInput As Integer
  1234.     Dim bMoveNext As Integer
  1235.     Dim bCancel As Integer
  1236.     Dim iNextInput As Integer
  1237.    
  1238.     ' FOR PRINTING OUTPUT
  1239.     DIM iDigits AS INTEGER ' # digits to display (values are truncated to this length)
  1240.     Dim iColCount As Integer
  1241.     Dim iGroupCount As Integer
  1242.     Dim sLine As String
  1243.     Dim iCols As Integer
  1244.     Dim iRows As Integer
  1245.     Dim iMaxCols As Integer
  1246.    
  1247.     ' INITIALIZE
  1248.     if len(sError) = 0 then
  1249.         iDigits = 4 ' 11
  1250.         iColCount = 3
  1251.         iGroupCount = 0 ' re-initialized at the top of every loop
  1252.         iCols = _WIDTH(0) \ _FontWidth
  1253.         iRows = _HEIGHT(0) \ _FontHeight
  1254.     end if
  1255.    
  1256.     ' COUNT # OF JOYSTICKS
  1257.     ' TODO: find out the right way to count joysticks
  1258.     if len(sError) = 0 then
  1259.         ' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
  1260.         iDeviceCount = _DEVICES ' Find the number of devices on someone's system
  1261.        
  1262.         IF iDeviceCount > 2 THEN
  1263.             ' LIMIT # OF DEVICES, IF THERE IS A LIMIT DEFINED
  1264.             iNumControllers = iDeviceCount - 2
  1265.             IF cMaxControllers > 0 THEN
  1266.                 IF iNumControllers > cMaxControllers THEN
  1267.                     iNumControllers = cMaxControllers
  1268.                 END IF
  1269.             END IF
  1270.         ELSE
  1271.             ' ONLY 2 FOUND (KEYBOARD, MOUSE)
  1272.             sError = "No game controllers found."
  1273.         END IF
  1274.     end if
  1275.    
  1276.     ' INITIALIZE CONTROLLER DATA
  1277.     if len(sError) = 0 then
  1278.         FOR iController = 1 TO iNumControllers
  1279.             m_arrController(iController).buttonCount = cMaxButtons
  1280.             m_arrController(iController).axisCount = cMaxAxis
  1281.             for iLoop = 1 to cMaxButtons
  1282.                 arrButtonNew(iController, iLoop) = TRUE
  1283.             next iloop
  1284.             for iLoop = 1 to cMaxAxis
  1285.                 arrAxisNew(iController, iLoop) = TRUE
  1286.             next iLoop
  1287.         NEXT iController
  1288.     end if
  1289.    
  1290.     ' INITIALIZE CONTROLLER INPUT
  1291.     if len(sError) = 0 then
  1292.         CLS
  1293.         Print "We will now detect controllers."
  1294.         Print "Do not touch any keys or game controllers during detection."
  1295.         Input "Press <ENTER> to begin";in$
  1296.         _KeyClear : Print
  1297.         sLine = "Initializing controllers" : Print sLine;
  1298.         iMaxCols = (iCols - len(sLine)) - 1
  1299.         iCount = 0
  1300.         DO
  1301.             iCount = iCount + 1
  1302.             if iCount < iMaxCols then
  1303.                 Print ".";
  1304.             else
  1305.                 Print "." : Print sLine : iCount = 0
  1306.             end if
  1307.             FOR iController = 1 TO iNumControllers
  1308.                 iDevice = iController + 2
  1309.                 WHILE _DEVICEINPUT(iDevice) ' clear and update the device buffer
  1310.                     FOR iLoop = 1 TO _LASTBUTTON(iDevice)
  1311.                         IF (iLoop > cMaxButtons) THEN EXIT FOR
  1312.                         m_arrController(iController).buttonCount = iLoop
  1313.                         'IF _BUTTONCHANGE(iLoop) THEN
  1314.                         '    arrButton(iController, iLoop) = _BUTTON(iLoop)
  1315.                         'END IF
  1316.                         arrButton(iController, iLoop) = FALSE
  1317.                     NEXT iLoop
  1318.                     FOR iLoop = 1 TO _LASTAXIS(iDevice) ' this loop checks all my axis
  1319.                         IF (iLoop > cMaxAxis) THEN EXIT FOR
  1320.                         m_arrController(iController).axisCount = iLoop
  1321.                         arrAxis(iController, iLoop) = 0
  1322.                     NEXT iLoop
  1323.                 WEND ' clear and update the device buffer
  1324.             NEXT iController
  1325.             _LIMIT 30
  1326.         LOOP UNTIL iCount > 60 ' quit after 2 seconds
  1327.         Print : Print
  1328.     end if
  1329.    
  1330.     ' WAIT FOR INPUT
  1331.     if len(sError) = 0 then
  1332.         CLS
  1333.         Print "Press <ESCAPE> to cancel at any time."
  1334.         Print
  1335.        
  1336.         _KEYCLEAR: _DELAY 1
  1337.         bCancel = FALSE
  1338.         bFinished = FALSE
  1339.         iLastPressed = 0
  1340.         For iWhichInput = lbound(m_arrControlMap, 2) to ubound(m_arrControlMap, 2)
  1341.             'print "iWhichInput=" + cstr$(iWhichInput)
  1342.             Print "Player #" + cstr$(iPlayer) + " press control for " + InputToString$(iWhichInput) + " or ESC to skip: ";
  1343.            
  1344.             ' =============================================================================
  1345.             ' BEGIN LOOK FOR NEXT INPUT
  1346.             bMoveNext = FALSE
  1347.             DO
  1348.                 ' -----------------------------------------------------------------------------
  1349.                 ' BEGIN CHECK FOR CONTROLLER INPUT
  1350.                 FOR iController = 1 TO iNumControllers
  1351.                     iDevice = iController + 2
  1352.                    
  1353.                     ' Check all devices
  1354.                     WHILE _DEVICEINPUT(iDevice)
  1355.                        
  1356.                         ' Check each button
  1357.                         If bMoveNext = FALSE Then
  1358.                             FOR iLoop = 1 TO _LASTBUTTON(iDevice)
  1359.                                 IF (iLoop > cMaxButtons) THEN EXIT FOR
  1360.                                 'm_arrController(iController).buttonCount = iLoop
  1361.                                
  1362.                                 ' update button array to indicate if a button is up or down currently.
  1363.                                 IF _BUTTONCHANGE(iLoop) THEN
  1364.                                     iValue = _BUTTON(iLoop)
  1365.                                     if iValue <> arrButton(iController, iLoop) then
  1366.                                         ' *****************************************************************************
  1367.                                         ' PRESSED BUTTON
  1368.                                        
  1369.                                         ' make sure this isn't already mapped
  1370.                                         bHaveInput = TRUE
  1371.                                         if iWhichInput > lbound(m_arrControlMap, 2) then
  1372.                                             ' is input unique?
  1373.                                             For iNextInput = lbound(m_arrControlMap, 2) to iWhichInput-1
  1374.                                                 if m_arrControlMap(iPlayer, iNextInput).device = iDevice then
  1375.                                                     if m_arrControlMap(iPlayer, iNextInput).typ = cInputButton then
  1376.                                                         if m_arrControlMap(iPlayer, iNextInput).code = iLoop then
  1377.                                                             if m_arrControlMap(iPlayer, iNextInput).value = iValue then
  1378.                                                                 bHaveInput = FALSE
  1379.                                                             end if
  1380.                                                         end if
  1381.                                                     end if
  1382.                                                 end if
  1383.                                             Next iNextInput
  1384.                                         end if
  1385.                                        
  1386.                                         if bHaveInput then
  1387.                                             m_arrControlMap(iPlayer, iWhichInput).device = iDevice
  1388.                                             m_arrControlMap(iPlayer, iWhichInput).typ = cInputButton
  1389.                                             m_arrControlMap(iPlayer, iWhichInput).code = iLoop
  1390.                                             m_arrControlMap(iPlayer, iWhichInput).value = iValue
  1391.                                             bMoveNext = TRUE
  1392.                                         end if
  1393.                                        
  1394.                                     end if
  1395.                                 END IF
  1396.                             NEXT iLoop
  1397.                         End If
  1398.                        
  1399.                         ' Check each axis
  1400.                         If bMoveNext = FALSE Then
  1401.                             FOR iLoop = 1 TO _LASTAXIS(iDevice)
  1402.                                 IF (iLoop > cMaxAxis) THEN EXIT FOR
  1403.                                 'm_arrController(iController).axisCount = iLoop
  1404.                                
  1405.                                 dblNextAxis = _AXIS(iLoop)
  1406.                                 dblNextAxis = RoundUpDouble# (dblNextAxis, 3)
  1407.                                
  1408.                                 ' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
  1409.                                 ' which is prone to always give minute values and never really center on true 0.
  1410.                                 ' A value of 1 means my axis is pushed fully in one direction.
  1411.                                 ' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
  1412.                                 ' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
  1413.                                
  1414.                                 'These are way too sensitive for analog:
  1415.                                 'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
  1416.                                 'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
  1417.                                 'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .001 THEN
  1418.                                
  1419.                                 'For digital input, we'll use a big picture:
  1420.                                 IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= 0.75 THEN
  1421.                                     if dblNextAxis <> arrAxis(iController, iLoop) then
  1422.                                         ' *****************************************************************************
  1423.                                         ' MOVED STICK
  1424.                                        
  1425.                                         ' convert to a digital value
  1426.                                         if dblNextAxis < 0 then
  1427.                                             iValue = -1
  1428.                                         else
  1429.                                             iValue = 1
  1430.                                         end if
  1431.                                        
  1432.                                         ' make sure this isn't already mapped
  1433.                                         bHaveInput = TRUE
  1434.                                         if iWhichInput > lbound(m_arrControlMap, 2) then
  1435.                                             ' is input unique?
  1436.                                             For iNextInput = lbound(m_arrControlMap, 2) to iWhichInput-1
  1437.                                                 if m_arrControlMap(iPlayer, iNextInput).device = iDevice then
  1438.                                                     if m_arrControlMap(iPlayer, iNextInput).typ = cInputAxis then
  1439.                                                         if m_arrControlMap(iPlayer, iNextInput).code = iLoop then
  1440.                                                             if m_arrControlMap(iPlayer, iNextInput).value = iValue then
  1441.                                                                 bHaveInput = FALSE
  1442.                                                             end if
  1443.                                                         end if
  1444.                                                     end if
  1445.                                                 end if
  1446.                                             Next iNextInput
  1447.                                         end if
  1448.                                        
  1449.                                         if bHaveInput then
  1450.                                             m_arrControlMap(iPlayer, iWhichInput).device = iDevice
  1451.                                             m_arrControlMap(iPlayer, iWhichInput).typ = cInputAxis
  1452.                                             m_arrControlMap(iPlayer, iWhichInput).code = iLoop
  1453.                                             m_arrControlMap(iPlayer, iWhichInput).value = iValue
  1454.                                             bMoveNext = TRUE
  1455.                                         end if
  1456.                                        
  1457.                                     end if
  1458.                                 END IF
  1459.                             NEXT iLoop
  1460.                         End If
  1461.                        
  1462.                     WEND ' clear and update the device buffer
  1463.                    
  1464.                 NEXT iController
  1465.                 ' END CHECK FOR CONTROLLER INPUT
  1466.                 ' -----------------------------------------------------------------------------
  1467.                
  1468.                 ' -----------------------------------------------------------------------------
  1469.                 ' BEGIN CHECK FOR KEYBOARD INPUT #1
  1470.                 If bMoveNext = FALSE Then
  1471.                     '_KEYCLEAR: _DELAY 1
  1472.                     WHILE _DEVICEINPUT(1): WEND ' clear and update the keyboard buffer
  1473.                    
  1474.                     ' Detect changed key state
  1475.                     For iLoop = lbound(m_arrButtonCode) to ubound(m_arrButtonCode)
  1476.                         iCode = m_arrButtonCode(iLoop)
  1477.                         If _BUTTON(iCode) <> FALSE Then
  1478.                             ' *****************************************************************************
  1479.                             ' PRESSED KEYBOARD
  1480.                             'PRINT "PRESSED " + m_arrButtonKey(iLoop)
  1481.                            
  1482.                             ' make sure this isn't already mapped
  1483.                             bHaveInput = TRUE
  1484.                             if iWhichInput > lbound(m_arrControlMap, 2) then
  1485.                                 ' is input unique?
  1486.                                 For iNextInput = lbound(m_arrControlMap, 2) to iWhichInput-1
  1487.                                     if m_arrControlMap(iPlayer, iNextInput).device = 1 then ' .device 1 = keyboard
  1488.                                         if m_arrControlMap(iPlayer, iNextInput).typ = cInputKey then
  1489.                                             if m_arrControlMap(iPlayer, iNextInput).code = iCode then
  1490.                                                 'if m_arrControlMap(iPlayer, iNextInput).value = TRUE then
  1491.                                                     bHaveInput = FALSE
  1492.                                                 'end if
  1493.                                             end if
  1494.                                         end if
  1495.                                     end if
  1496.                                 Next iNextInput
  1497.                             end if
  1498.                            
  1499.                             if bHaveInput then
  1500.                                 m_arrControlMap(iPlayer, iWhichInput).device = 1 ' .device 1 = keyboard
  1501.                                 m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey
  1502.                                 m_arrControlMap(iPlayer, iWhichInput).code = iCode
  1503.                                 m_arrControlMap(iPlayer, iWhichInput).value = TRUE
  1504.                                 bMoveNext = TRUE
  1505.                             end if
  1506.                            
  1507.                         End If
  1508.                     Next iLoop
  1509.                 End If
  1510.                 ' END CHECK FOR KEYBOARD INPUT #1
  1511.                 ' -----------------------------------------------------------------------------
  1512.                
  1513.                 IF bMoveNext = TRUE THEN EXIT DO
  1514.                 _LIMIT 30
  1515.             LOOP UNTIL _KEYHIT = 27 ' ESCAPE to quit
  1516.             ' END LOOK FOR NEXT INPUT
  1517.             ' =============================================================================
  1518.            
  1519.             IF bMoveNext = TRUE THEN
  1520.                 Print "Device #" + cstr$(m_arrControlMap(iPlayer, iWhichInput).device) + " " + _
  1521.                     InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) + " " + _
  1522.                     cstr$(m_arrControlMap(iPlayer, iWhichInput).code) + " = " + _
  1523.                     cstr$(m_arrControlMap(iPlayer, iWhichInput).value)
  1524.             ELSE
  1525.                 PRINT "(Skipped)"
  1526.                 bCancel = TRUE
  1527.                 bFinished = TRUE
  1528.             END IF
  1529.            
  1530.             if bFinished = TRUE then Exit For
  1531.         next iWhichInput
  1532.     end if
  1533.    
  1534.     if len(sError) > 0 then
  1535.         sResult = "ERRORS: " + sError
  1536.     end if
  1537.    
  1538.     _KEYCLEAR: _DELAY 1
  1539.     MapInput1$ = sResult
  1540. END SUB ' MapInput1$
  1541.  
  1542. ' /////////////////////////////////////////////////////////////////////////////
  1543.  
  1544. Function InputToString$(iCode As Integer)
  1545.     Select Case iCode
  1546.         Case cInputUp:
  1547.             InputToString$ = "up"
  1548.         Case cInputDown:
  1549.             InputToString$ = "down"
  1550.         Case cInputLeft:
  1551.             InputToString$ = "left"
  1552.         Case cInputRight:
  1553.             InputToString$ = "right"
  1554.         Case cInputButton1:
  1555.             InputToString$ = "button #1"
  1556.         Case cInputButton2:
  1557.             InputToString$ = "button #2"
  1558.         Case cInputButton3:
  1559.             InputToString$ = "button #3"
  1560.         Case cInputButton4:
  1561.             InputToString$ = "button #4"
  1562.         Case Else:
  1563.             InputToString$ = "unknown"
  1564.     End Select
  1565. End Function ' InputToString$
  1566.  
  1567. ' /////////////////////////////////////////////////////////////////////////////
  1568.  
  1569. Function InputTypeToString$(iCode As Integer)
  1570.     Select Case iCode
  1571.         Case cInputNone:
  1572.             InputTypeToString$ = "none"
  1573.         Case cInputKey:
  1574.             InputTypeToString$ = "key"
  1575.         Case cInputButton:
  1576.             InputTypeToString$ = "button"
  1577.         Case cInputAxis:
  1578.             InputTypeToString$ = "axis"
  1579.         Case Else:
  1580.             InputTypeToString$ = "unknown"
  1581.     End Select
  1582. End Function ' InputTypeToString$
  1583.  
  1584. ' /////////////////////////////////////////////////////////////////////////////
  1585. ' METHOD v2 = faster
  1586.  
  1587. Function GetKeyboardButtonCodeText$(iCode As Integer)
  1588.     Dim sResult As String : sResult = ""
  1589.     If lbound(m_arrButtonKeyDesc) <= iCode Then
  1590.         If ubound(m_arrButtonKeyDesc) >= iCode Then
  1591.             sResult = m_arrButtonKeyDesc(iCode)
  1592.         End If
  1593.     End If
  1594.     If Len(sResult) = 0 Then
  1595.         sResult = _TRIM$(STR$(iCode)) + " (?)"
  1596.     End If
  1597.     GetKeyboardButtonCodeText$ = sResult
  1598. End Function ' GetKeyboardButtonCodeText$
  1599.  
  1600. ' /////////////////////////////////////////////////////////////////////////////
  1601. ' METHOD v2
  1602. ' Faster lookup - a dictionary with a hash lookup would be best
  1603. ' but this is a quick way to do it since the values never change.
  1604.  
  1605. ' The following shared arrays must be declared:
  1606. '     ReDim Shared m_arrButtonCode(1 To 99) As Long
  1607. '     ReDim Shared m_arrButtonKey(1 To 99) As String
  1608. '     ReDim Shared m_arrButtonKeyDesc(0 To 512) As String
  1609.  
  1610. Sub InitKeyboardButtonCodes()
  1611.     Dim iLoop As Integer
  1612.    
  1613.     ' CODE(S) DETECTED WITH _BUTTON:
  1614.     m_arrButtonCode(1) = 2 : m_arrButtonKey(1) = "Esc"
  1615.     m_arrButtonCode(2) = 60 : m_arrButtonKey(2) = "F1"
  1616.     m_arrButtonCode(3) = 61 : m_arrButtonKey(3) = "F2"
  1617.     m_arrButtonCode(4) = 62 : m_arrButtonKey(4) = "F3"
  1618.     m_arrButtonCode(5) = 63 : m_arrButtonKey(5) = "F4"
  1619.     m_arrButtonCode(6) = 64 : m_arrButtonKey(6) = "F5"
  1620.     m_arrButtonCode(7) = 65 : m_arrButtonKey(7) = "F6"
  1621.     m_arrButtonCode(8) = 66 : m_arrButtonKey(8) = "F7"
  1622.     m_arrButtonCode(9) = 67 : m_arrButtonKey(9) = "F8"
  1623.     m_arrButtonCode(10) = 68 : m_arrButtonKey(10) = "F9"
  1624.     m_arrButtonCode(11) = 88 : m_arrButtonKey(11) = "F11"
  1625.     m_arrButtonCode(12) = 89 : m_arrButtonKey(12) = "F12"
  1626.     m_arrButtonCode(13) = 42 : m_arrButtonKey(13) = "Tilde"
  1627.     m_arrButtonCode(14) = 3 : m_arrButtonKey(14) = "1"
  1628.     m_arrButtonCode(15) = 4 : m_arrButtonKey(15) = "2"
  1629.     m_arrButtonCode(16) = 5 : m_arrButtonKey(16) = "3"
  1630.     m_arrButtonCode(17) = 6 : m_arrButtonKey(17) = "4"
  1631.     m_arrButtonCode(18) = 7 : m_arrButtonKey(18) = "5"
  1632.     m_arrButtonCode(19) = 8 : m_arrButtonKey(19) = "6"
  1633.     m_arrButtonCode(20) = 9 : m_arrButtonKey(20) = "7"
  1634.     m_arrButtonCode(21) = 10 : m_arrButtonKey(21) = "8"
  1635.     m_arrButtonCode(22) = 11 : m_arrButtonKey(22) = "9"
  1636.     m_arrButtonCode(23) = 12 : m_arrButtonKey(23) = "0"
  1637.     m_arrButtonCode(24) = 13 : m_arrButtonKey(24) = "Minus"
  1638.     m_arrButtonCode(25) = 14 : m_arrButtonKey(25) = "Equal"
  1639.     m_arrButtonCode(26) = 15 : m_arrButtonKey(26) = "BkSp"
  1640.     m_arrButtonCode(27) = 16 : m_arrButtonKey(27) = "Tab"
  1641.     m_arrButtonCode(28) = 17 : m_arrButtonKey(28) = "Q"
  1642.     m_arrButtonCode(29) = 18 : m_arrButtonKey(29) = "W"
  1643.     m_arrButtonCode(30) = 19 : m_arrButtonKey(30) = "E"
  1644.     m_arrButtonCode(31) = 20 : m_arrButtonKey(31) = "R"
  1645.     m_arrButtonCode(32) = 21 : m_arrButtonKey(32) = "T"
  1646.     m_arrButtonCode(33) = 22 : m_arrButtonKey(33) = "Y"
  1647.     m_arrButtonCode(34) = 23 : m_arrButtonKey(34) = "U"
  1648.     m_arrButtonCode(35) = 24 : m_arrButtonKey(35) = "I"
  1649.     m_arrButtonCode(36) = 25 : m_arrButtonKey(36) = "O"
  1650.     m_arrButtonCode(37) = 26 : m_arrButtonKey(37) = "P"
  1651.     m_arrButtonCode(38) = 27 : m_arrButtonKey(38) = "BracketLeft"
  1652.     m_arrButtonCode(39) = 28 : m_arrButtonKey(39) = "BracketRight"
  1653.     m_arrButtonCode(40) = 44 : m_arrButtonKey(40) = "Backslash"
  1654.     m_arrButtonCode(41) = 59 : m_arrButtonKey(41) = "CapsLock"
  1655.     m_arrButtonCode(42) = 31 : m_arrButtonKey(42) = "A"
  1656.     m_arrButtonCode(43) = 32 : m_arrButtonKey(43) = "S"
  1657.     m_arrButtonCode(44) = 33 : m_arrButtonKey(44) = "D"
  1658.     m_arrButtonCode(45) = 34 : m_arrButtonKey(45) = "F"
  1659.     m_arrButtonCode(46) = 35 : m_arrButtonKey(46) = "G"
  1660.     m_arrButtonCode(47) = 36 : m_arrButtonKey(47) = "H"
  1661.     m_arrButtonCode(48) = 37 : m_arrButtonKey(48) = "J"
  1662.     m_arrButtonCode(49) = 38 : m_arrButtonKey(49) = "K"
  1663.     m_arrButtonCode(50) = 39 : m_arrButtonKey(50) = "L"
  1664.     m_arrButtonCode(51) = 40 : m_arrButtonKey(51) = "Semicolon"
  1665.     m_arrButtonCode(52) = 41 : m_arrButtonKey(52) = "Apostrophe"
  1666.     m_arrButtonCode(53) = 29 : m_arrButtonKey(53) = "Enter"
  1667.     m_arrButtonCode(54) = 43 : m_arrButtonKey(54) = "ShiftLeft"
  1668.     m_arrButtonCode(55) = 45 : m_arrButtonKey(55) = "Z"
  1669.     m_arrButtonCode(56) = 46 : m_arrButtonKey(56) = "X"
  1670.     m_arrButtonCode(57) = 47 : m_arrButtonKey(57) = "C"
  1671.     m_arrButtonCode(58) = 48 : m_arrButtonKey(58) = "V"
  1672.     m_arrButtonCode(59) = 49 : m_arrButtonKey(59) = "B"
  1673.     m_arrButtonCode(60) = 50 : m_arrButtonKey(60) = "N"
  1674.     m_arrButtonCode(61) = 51 : m_arrButtonKey(61) = "M"
  1675.     m_arrButtonCode(62) = 52 : m_arrButtonKey(62) = "Comma"
  1676.     m_arrButtonCode(63) = 53 : m_arrButtonKey(63) = "Period"
  1677.     m_arrButtonCode(64) = 54 : m_arrButtonKey(64) = "Slash"
  1678.     m_arrButtonCode(65) = 55 : m_arrButtonKey(65) = "ShiftRight"
  1679.     m_arrButtonCode(66) = 30 : m_arrButtonKey(66) = "CtrlLeft"
  1680.     m_arrButtonCode(67) = 348 : m_arrButtonKey(67) = "WinLeft"
  1681.     m_arrButtonCode(68) = 58 : m_arrButtonKey(68) = "Spacebar"
  1682.     m_arrButtonCode(69) = 349 : m_arrButtonKey(69) = "WinRight"
  1683.     m_arrButtonCode(70) = 350 : m_arrButtonKey(70) = "Menu"
  1684.     m_arrButtonCode(71) = 286 : m_arrButtonKey(71) = "CtrlRight"
  1685.     m_arrButtonCode(72) = 339 : m_arrButtonKey(72) = "Ins"
  1686.     m_arrButtonCode(73) = 328 : m_arrButtonKey(73) = "Home"
  1687.     m_arrButtonCode(74) = 330 : m_arrButtonKey(74) = "PgUp"
  1688.     m_arrButtonCode(75) = 340 : m_arrButtonKey(75) = "Del"
  1689.     m_arrButtonCode(76) = 336 : m_arrButtonKey(76) = "End"
  1690.     m_arrButtonCode(77) = 338 : m_arrButtonKey(77) = "PgDn"
  1691.     m_arrButtonCode(78) = 329 : m_arrButtonKey(78) = "Up"
  1692.     m_arrButtonCode(79) = 332 : m_arrButtonKey(79) = "Left"
  1693.     m_arrButtonCode(80) = 337 : m_arrButtonKey(80) = "Down"
  1694.     m_arrButtonCode(81) = 334 : m_arrButtonKey(81) = "Right"
  1695.     m_arrButtonCode(82) = 71 : m_arrButtonKey(82) = "ScrollLock"
  1696.     m_arrButtonCode(83) = 326 : m_arrButtonKey(83) = "NumLock"
  1697.     m_arrButtonCode(84) = 310 : m_arrButtonKey(84) = "KeypadSlash"
  1698.     m_arrButtonCode(85) = 56 : m_arrButtonKey(85) = "KeypadMultiply"
  1699.     m_arrButtonCode(86) = 75 : m_arrButtonKey(86) = "KeypadMinus"
  1700.     m_arrButtonCode(87) = 72 : m_arrButtonKey(87) = "Keypad7Home"
  1701.     m_arrButtonCode(88) = 73 : m_arrButtonKey(88) = "Keypad8Up"
  1702.     m_arrButtonCode(89) = 74 : m_arrButtonKey(89) = "Keypad9PgUp"
  1703.     m_arrButtonCode(90) = 79 : m_arrButtonKey(90) = "KeypadPlus"
  1704.     m_arrButtonCode(91) = 76 : m_arrButtonKey(91) = "Keypad4Left"
  1705.     m_arrButtonCode(92) = 77 : m_arrButtonKey(92) = "Keypad5"
  1706.     m_arrButtonCode(93) = 78 : m_arrButtonKey(93) = "Keypad6Right"
  1707.     m_arrButtonCode(94) = 80 : m_arrButtonKey(94) = "Keypad1End"
  1708.     m_arrButtonCode(95) = 81 : m_arrButtonKey(95) = "Keypad2Down"
  1709.     m_arrButtonCode(96) = 82 : m_arrButtonKey(96) = "Keypad3PgDn"
  1710.     m_arrButtonCode(97) = 285 : m_arrButtonKey(97) = "KeypadEnter"
  1711.     m_arrButtonCode(98) = 83 : m_arrButtonKey(98) = "Keypad0Ins"
  1712.     m_arrButtonCode(99) = 84 : m_arrButtonKey(99) = "KeypadPeriodDel"
  1713.    
  1714. ' not sure if this works:
  1715.     '' CODE(S) DETECTED WITH _KEYDOWN:
  1716.     'm_arrButtonCode(100) = -1 : m_arrButtonCode(100) = "F10"
  1717.    
  1718. ' not sure if this works:
  1719.     '' CODE(S) DETECTED WITH _KEYHIT:
  1720.     'm_arrButtonCode(101) = -2 : m_arrButtonCode(101) = "AltLeft"
  1721.     'm_arrButtonCode(102) = -3 : m_arrButtonCode(102) = "AltRight"
  1722.    
  1723.     ' DESCRIPTIONS BY KEYCODE
  1724.     For iLoop = lbound(m_arrButtonKeyDesc) to ubound (m_arrButtonKeyDesc)
  1725.         m_arrButtonKeyDesc(iLoop) = ""
  1726.     Next iLoop
  1727.     m_arrButtonKeyDesc(2) = "Esc"
  1728.     m_arrButtonKeyDesc(60) = "F1"
  1729.     m_arrButtonKeyDesc(61) = "F2"
  1730.     m_arrButtonKeyDesc(62) = "F3"
  1731.     m_arrButtonKeyDesc(63) = "F4"
  1732.     m_arrButtonKeyDesc(64) = "F5"
  1733.     m_arrButtonKeyDesc(65) = "F6"
  1734.     m_arrButtonKeyDesc(66) = "F7"
  1735.     m_arrButtonKeyDesc(67) = "F8"
  1736.     m_arrButtonKeyDesc(68) = "F9"
  1737.     m_arrButtonKeyDesc(88) = "F11"
  1738.     m_arrButtonKeyDesc(89) = "F12"
  1739.     m_arrButtonKeyDesc(42) = "Tilde"
  1740.     m_arrButtonKeyDesc(3) = "1"
  1741.     m_arrButtonKeyDesc(4) = "2"
  1742.     m_arrButtonKeyDesc(5) = "3"
  1743.     m_arrButtonKeyDesc(6) = "4"
  1744.     m_arrButtonKeyDesc(7) = "5"
  1745.     m_arrButtonKeyDesc(8) = "6"
  1746.     m_arrButtonKeyDesc(9) = "7"
  1747.     m_arrButtonKeyDesc(10) = "8"
  1748.     m_arrButtonKeyDesc(11) = "9"
  1749.     m_arrButtonKeyDesc(12) = "0"
  1750.     m_arrButtonKeyDesc(13) = "Minus"
  1751.     m_arrButtonKeyDesc(14) = "Equal"
  1752.     m_arrButtonKeyDesc(15) = "BkSp"
  1753.     m_arrButtonKeyDesc(16) = "Tab"
  1754.     m_arrButtonKeyDesc(17) = "Q"
  1755.     m_arrButtonKeyDesc(18) = "W"
  1756.     m_arrButtonKeyDesc(19) = "E"
  1757.     m_arrButtonKeyDesc(20) = "R"
  1758.     m_arrButtonKeyDesc(21) = "T"
  1759.     m_arrButtonKeyDesc(22) = "Y"
  1760.     m_arrButtonKeyDesc(23) = "U"
  1761.     m_arrButtonKeyDesc(24) = "I"
  1762.     m_arrButtonKeyDesc(25) = "O"
  1763.     m_arrButtonKeyDesc(26) = "P"
  1764.     m_arrButtonKeyDesc(27) = "BracketLeft"
  1765.     m_arrButtonKeyDesc(28) = "BracketRight"
  1766.     m_arrButtonKeyDesc(44) = "Backslash"
  1767.     m_arrButtonKeyDesc(59) = "CapsLock"
  1768.     m_arrButtonKeyDesc(31) = "A"
  1769.     m_arrButtonKeyDesc(32) = "S"
  1770.     m_arrButtonKeyDesc(33) = "D"
  1771.     m_arrButtonKeyDesc(34) = "F"
  1772.     m_arrButtonKeyDesc(35) = "G"
  1773.     m_arrButtonKeyDesc(36) = "H"
  1774.     m_arrButtonKeyDesc(37) = "J"
  1775.     m_arrButtonKeyDesc(38) = "K"
  1776.     m_arrButtonKeyDesc(39) = "L"
  1777.     m_arrButtonKeyDesc(40) = "Semicolon"
  1778.     m_arrButtonKeyDesc(41) = "Apostrophe"
  1779.     m_arrButtonKeyDesc(29) = "Enter"
  1780.     m_arrButtonKeyDesc(43) = "ShiftLeft"
  1781.     m_arrButtonKeyDesc(45) = "Z"
  1782.     m_arrButtonKeyDesc(46) = "X"
  1783.     m_arrButtonKeyDesc(47) = "C"
  1784.     m_arrButtonKeyDesc(48) = "V"
  1785.     m_arrButtonKeyDesc(49) = "B"
  1786.     m_arrButtonKeyDesc(50) = "N"
  1787.     m_arrButtonKeyDesc(51) = "M"
  1788.     m_arrButtonKeyDesc(52) = "Comma"
  1789.     m_arrButtonKeyDesc(53) = "Period"
  1790.     m_arrButtonKeyDesc(54) = "Slash"
  1791.     m_arrButtonKeyDesc(55) = "ShiftRight"
  1792.     m_arrButtonKeyDesc(30) = "CtrlLeft"
  1793.     m_arrButtonKeyDesc(348) = "WinLeft"
  1794.     m_arrButtonKeyDesc(58) = "Spacebar"
  1795.     m_arrButtonKeyDesc(349) = "WinRight"
  1796.     m_arrButtonKeyDesc(350) = "Menu"
  1797.     m_arrButtonKeyDesc(286) = "CtrlRight"
  1798.     m_arrButtonKeyDesc(339) = "Ins"
  1799.     m_arrButtonKeyDesc(328) = "Home"
  1800.     m_arrButtonKeyDesc(330) = "PgUp"
  1801.     m_arrButtonKeyDesc(340) = "Del"
  1802.     m_arrButtonKeyDesc(336) = "End"
  1803.     m_arrButtonKeyDesc(338) = "PgDn"
  1804.     m_arrButtonKeyDesc(329) = "Up"
  1805.     m_arrButtonKeyDesc(332) = "Left"
  1806.     m_arrButtonKeyDesc(337) = "Down"
  1807.     m_arrButtonKeyDesc(334) = "Right"
  1808.     m_arrButtonKeyDesc(71) = "ScrollLock"
  1809.     m_arrButtonKeyDesc(326) = "NumLock"
  1810.     m_arrButtonKeyDesc(310) = "KeypadSlash"
  1811.     m_arrButtonKeyDesc(56) = "KeypadMultiply"
  1812.     m_arrButtonKeyDesc(75) = "KeypadMinus"
  1813.     m_arrButtonKeyDesc(72) = "Keypad7Home"
  1814.     m_arrButtonKeyDesc(73) = "Keypad8Up"
  1815.     m_arrButtonKeyDesc(74) = "Keypad9PgUp"
  1816.     m_arrButtonKeyDesc(79) = "KeypadPlus"
  1817.     m_arrButtonKeyDesc(76) = "Keypad4Left"
  1818.     m_arrButtonKeyDesc(77) = "Keypad5"
  1819.     m_arrButtonKeyDesc(78) = "Keypad6Right"
  1820.     m_arrButtonKeyDesc(80) = "Keypad1End"
  1821.     m_arrButtonKeyDesc(81) = "Keypad2Down"
  1822.     m_arrButtonKeyDesc(82) = "Keypad3PgDn"
  1823.     m_arrButtonKeyDesc(285) = "KeypadEnter"
  1824.     m_arrButtonKeyDesc(83) = "Keypad0Ins"
  1825.     m_arrButtonKeyDesc(84) = "KeypadPeriodDel"
  1826. End Sub ' InitKeyboardButtonCodes
  1827.  
  1828. ' /////////////////////////////////////////////////////////////////////////////
  1829. ' not sure if this works
  1830.  
  1831. ' Returns TRUE if the F10 key is held down.
  1832. ' We use _KEYDOWN for this because _BUTTON doesn't detect F10.
  1833.  
  1834. ' Constant must be declared globally:
  1835. ' Const c_iKeyDown_F10 = 17408
  1836.  
  1837. Function KeydownF10%
  1838.     Dim iCode AS Long
  1839.     '_KEYCLEAR: _DELAY 1
  1840.     If _KEYDOWN(c_iKeyDown_F10) = TRUE THEN
  1841.         KeydownF10% = TRUE
  1842.     Else
  1843.         KeydownF10% = FALSE
  1844.     End If
  1845.     '_KEYCLEAR
  1846. End Function ' KeydownF10%
  1847.  
  1848. ' /////////////////////////////////////////////////////////////////////////////
  1849. ' not sure if this works
  1850.  
  1851. ' Returns TRUE if the left ALT key is held down.
  1852. ' We use _KEYHIT for this because _BUTTON doesn't detect ALT.
  1853.  
  1854. ' Constant must be declared globally:
  1855. ' Const c_iKeyHit_AltLeft = -30764    
  1856.  
  1857. Function KeyhitAltLeft%
  1858.     '_KEYCLEAR: _DELAY 1
  1859.     If _KEYHIT = c_iKeyHit_AltLeft Then
  1860.         KeyhitAltLeft% = TRUE
  1861.     Else
  1862.         KeyhitAltLeft% = FALSE
  1863.     End If
  1864.     '_KEYCLEAR
  1865. End Function ' KeyhitAltLeft%
  1866.  
  1867. ' /////////////////////////////////////////////////////////////////////////////
  1868. ' not sure if this works
  1869.  
  1870. ' Returns TRUE if the right ALT key is held down.
  1871. ' We use _KEYHIT for this because _BUTTON doesn't detect ALT.
  1872.  
  1873. ' Constant must be declared globally:
  1874. ' Const c_iKeyHit_AltRight = -30765
  1875.  
  1876. Function KeyhitAltRight%
  1877.     '_KEYCLEAR: _DELAY 1
  1878.     If _KEYHIT = c_iKeyHit_AltRight Then
  1879.         KeyhitAltRight% = TRUE
  1880.     Else
  1881.         KeyhitAltRight% = FALSE
  1882.     End If
  1883.     '_KEYCLEAR
  1884. End Function ' KeyhitAltRight%
  1885.  
  1886. ' /////////////////////////////////////////////////////////////////////////////
  1887. ' DEVICES Button
  1888. ' _LASTBUTTON(1) keyboards will normally return 512 buttons. One button is read per loop through all numbers.
  1889. ' _BUTTONCHANGE(number) returns -1 when pressed, 1 when released and 0 when there is no event since the last read.
  1890. ' _BUTTON(number) returns -1 when a button is pressed and 0 when released
  1891.  
  1892. ' Detects most keys (where the codes are documented?)
  1893.  
  1894. ' However, does not seem to detect:
  1895. ' Key             Use
  1896. ' ---             ---
  1897. ' F10             Function KeydownF10%
  1898. ' Left Alt        Function KeyhitAltLeft%
  1899. ' Right Alt       Function KeyhitAltRight%
  1900. ' Print Screen    (system API call?)
  1901. ' Pause/Break     (system API call?)
  1902.  
  1903. Function KeyPressed%(iCode As Integer)
  1904.     '_KEYCLEAR: _DELAY 1
  1905.     WHILE _DEVICEINPUT(1): WEND ' clear and update the keyboard buffer
  1906.     If _BUTTON(iCode) <> FALSE Then
  1907.         KeyPressed% = TRUE
  1908.     Else
  1909.         KeyPressed% = FALSE
  1910.     End If
  1911.     '_KEYCLEAR
  1912. End Function ' KeyPressed%
  1913.  
  1914. ' /////////////////////////////////////////////////////////////////////////////
  1915.  
  1916. Function TestJoysticks$
  1917.     Dim RoutineName As String: RoutineName = "TestJoysticks$"
  1918.     DIM iDeviceCount As Integer
  1919.     Dim sResult As String
  1920.    
  1921.     ' 1 is the keyboard
  1922.     ' 2 is the mouse
  1923.     ' 3 is the joystick
  1924.     ' unless someone has a strange setup with multiple mice/keyboards/ect...
  1925.     ' In that case, you can use _DEVICE$(i) to look for "KEYBOARD", "MOUSE", "JOYSTICK", if necessary.
  1926.     ' I've never actually found it necessary, but I figure it's worth mentioning, just in case...
  1927.    
  1928.     iDeviceCount = _DEVICES ' Find the number of devices on someone's system
  1929.     IF iDeviceCount > 2 THEN
  1930.         TestJoysticks1
  1931.         sResult = ""
  1932.     ELSE
  1933.         sResult = "No joysticks found."
  1934.     END IF
  1935.    
  1936.     _KeyClear
  1937.    
  1938.     TestJoysticks$ = sResult
  1939. END Function ' TestJoysticks$
  1940.  
  1941. ' /////////////////////////////////////////////////////////////////////////////
  1942. ' Reads controllers and displays values on screen.
  1943.  
  1944. ' Currently this is set up to support up to 8 joysticks,
  1945. ' with upto 4 buttons and 2 axes each
  1946. ' Testing with an old USB Logitech RumblePad 2
  1947. ' and Atari 2600 joysticks plugged into using
  1948. ' iCode Atari Joystick, Paddle, Driving to USB Adapter 4 ports
  1949.  
  1950. SUB TestJoysticks1 ()
  1951.     DIM RoutineName AS STRING:: RoutineName = "TestJoysticks1"
  1952.  
  1953.     DIM in$
  1954.     DIM iDeviceCount AS INTEGER
  1955.     DIM iDevice AS INTEGER
  1956.  
  1957.     DIM arrButton(32, 16) As Integer ' number of buttons on the joystick
  1958.     DIM arrButtonMin(32, 16) As Integer ' stores the minimum value read
  1959.     DIM arrButtonMax(32, 16) As Integer ' stores the maximum value read
  1960.     DIM arrAxis(32, 16) As Double ' number of axis on the joystick
  1961.     DIM arrAxisMin(32, 16) As Double ' stores the minimum value read
  1962.     DIM arrAxisMax(32, 16) As Double ' stores the maximum value read
  1963.     DIM arrAxisAvg(32, 16) As Double ' stores the average value read in the last few measurements
  1964.     DIM arrButtonNew(32, 16) As Integer ' tracks when to initialize values
  1965.     DIM arrAxisNew(32, 16) As Integer ' tracks when to initialize values
  1966.    
  1967.     DIM arrController(8) AS ControllerType ' holds info for each player
  1968.     DIM iNumControllers AS INTEGER
  1969.     DIM iController AS INTEGER
  1970.     DIM iNextY AS INTEGER
  1971.     DIM iNextX AS INTEGER
  1972.     DIM iNextC AS INTEGER
  1973.     DIM iLoop AS INTEGER
  1974.     DIM iDigits AS INTEGER ' # digits to display (values are truncated to this length)
  1975.     DIM strValue AS STRING
  1976.     Dim strAxis AS STRING
  1977.     DIM dblNextAxis
  1978.     'DIM iMeasureCount AS INTEGER
  1979.     DIM dblAverage AS DOUBLE
  1980.     DIM sngAverage AS SINGLE
  1981.     Dim sLine As String
  1982.     Dim iX As Integer
  1983.     Dim iY As Integer
  1984.    
  1985.     Dim iCol As Integer
  1986.     Dim iRow As Integer
  1987.     Dim iCols As Integer
  1988.     Dim iRows As Integer
  1989.     Dim iColWidth As Integer
  1990.     Dim iColCount As Integer
  1991.     Dim iGroupCount As Integer
  1992.    
  1993.     ' SET UP SCREEN
  1994.     Screen _NewImage(1280, 1024, 32): _ScreenMove 0, 0
  1995.    
  1996.     ' INITIALIZE
  1997.     iDigits = 4 ' 11
  1998.     iColCount = 3
  1999.     iCols = _WIDTH(0) \ _FontWidth
  2000.     iRows = _HEIGHT(0) \ _FontHeight
  2001.     iColWidth = iCols \ iColCount
  2002.    
  2003.     ' COUNT # OF JOYSTICKS
  2004.     ' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
  2005.     iDeviceCount = _DEVICES ' Find the number of devices on someone's system
  2006.     IF iDeviceCount < 3 THEN
  2007.         CLS
  2008.         PRINT "NO JOYSTICKS FOUND, EXITING..."
  2009.         INPUT "PRESS <ENTER>";in$
  2010.         EXIT SUB
  2011.     END IF
  2012.    
  2013.     ' BASE # OF PLAYERS ON HOW MANY CONTROLLERS FOUND
  2014.     iNumControllers = iDeviceCount - 2 ' TODO: find out the right way to count joysticks
  2015.     IF iNumControllers > cMaxControllers THEN
  2016.         iNumControllers = cMaxControllers
  2017.     END IF
  2018.    
  2019.     ' INITIALIZE PLAYER COORDINATES AND SCREEN CHARACTERS
  2020.     iNextY = 1
  2021.     iNextX = -3
  2022.     iNextC = 64
  2023.     FOR iController = 1 TO iNumControllers
  2024.         iNextX = iNextX + 4
  2025.         IF iNextX > 80 THEN
  2026.             iNextX = 1
  2027.             iNextY = iNextY + 4
  2028.         END IF
  2029.         iNextC = iNextC + 1
  2030.         arrController(iController).buttonCount = cMaxButtons
  2031.         arrController(iController).axisCount = cMaxAxis
  2032.        
  2033.         for iLoop = 1 to cMaxButtons
  2034.             arrButtonNew(iController, iLoop) = TRUE
  2035.         next iloop
  2036.         for iLoop = 1 to cMaxAxis
  2037.             arrAxisNew(iController, iLoop) = TRUE
  2038.             arrAxisAvg(iController, iLoop) = 0
  2039.         next iLoop
  2040.     NEXT iController
  2041.  
  2042.     ' CLEAR THE SCREEN
  2043.     'iMeasureCount = 0
  2044.     DO
  2045.         FOR iController = 1 TO iNumControllers
  2046.             iDevice = iController + 2
  2047.  
  2048.             WHILE _DEVICEINPUT(iDevice) ' clear and update the device buffer
  2049.                 ''IF _DEVICEINPUT = 3 THEN ' this says we only care about joystick input values
  2050.                
  2051.                 ' check all the buttons
  2052.                 FOR iLoop = 1 TO _LASTBUTTON(iDevice)
  2053.                     IF (iLoop > cMaxButtons) THEN
  2054.                         EXIT FOR
  2055.                     END IF
  2056.                     arrController(iController).buttonCount = iLoop
  2057.                    
  2058.                     ' update button array to indicate if a button is up or down currently.
  2059.                     IF _BUTTONCHANGE(iLoop) THEN
  2060.                         '' _BUTTON(number) returns -1 when a button is pressed and 0 when released.
  2061.                         ''arrButton(iLoop) = NOT arrButton(iLoop)
  2062.                         arrButton(iController, iLoop) = _BUTTON(iLoop)
  2063.                     END IF
  2064.                    
  2065.                     '' SAVE MINIMUM VALUE
  2066.                     'if arrButton(iController, iLoop) < arrButtonMin(iController, iLoop) then
  2067.                     '    arrButtonMin(iController, iLoop) = arrButton(iController, iLoop)
  2068.                     '    
  2069.                     '    ' INITIALIZE THE MAX TO THE MINIMUM VALUE
  2070.                     '    IF arrButtonNew(iController, iLoop) = TRUE THEN
  2071.                     '        arrButtonMax(iController, iLoop) = arrButtonMin(iController, iLoop)
  2072.                     '        arrButtonNew(iController, iLoop) = FALSE
  2073.                     '    END IF
  2074.                     'end if
  2075.                     '
  2076.                     '' SAVE MAXIMUM VALUE
  2077.                     'if arrButton(iController, iLoop) > arrButtonMax(iController, iLoop) then
  2078.                     '    arrButtonMax(iController, iLoop) = arrButton(iController, iLoop)
  2079.                     'end if
  2080.                    
  2081.                 NEXT iLoop
  2082.                
  2083.                 FOR iLoop = 1 TO _LASTAXIS(iDevice) ' this loop checks all my axis
  2084.                     IF (iLoop > cMaxAxis) THEN
  2085.                         EXIT FOR
  2086.                     END IF
  2087.                     arrController(iController).axisCount = iLoop
  2088.                    
  2089.                     ' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
  2090.                     ' which is prone to always give minute values and never really center on true 0.
  2091.                     ' A value of 1 means my axis is pushed fully in one direction.
  2092.                     ' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
  2093.                     ' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
  2094.                     'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
  2095.                    
  2096.                     dblNextAxis = _AXIS(iLoop)
  2097.                     dblNextAxis = RoundUpDouble# (dblNextAxis, 3)
  2098.                     'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
  2099.                     IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .001 THEN
  2100.                         arrAxis(iController, iLoop) = dblNextAxis
  2101.                     ELSE
  2102.                         arrAxis(iController, iLoop) = 0
  2103.                     END IF
  2104.                    
  2105.                     '' SAVE MINIMUM VALUE
  2106.                     'if arrAxis(iController, iLoop) < arrAxisMin(iController, iLoop) then
  2107.                     '    arrAxisMin(iController, iLoop) = arrAxis(iController, iLoop)
  2108.                     '    
  2109.                     '    ' INITIALIZE THE MAX TO THE MINIMUM VALUE
  2110.                     '    IF arrAxisNew(iController, iLoop) = TRUE THEN
  2111.                     '        arrAxisMax(iController, iLoop) = arrAxisMin(iController, iLoop)
  2112.                     '        arrAxisNew(iController, iLoop) = FALSE
  2113.                     '    END IF
  2114.                     'end if
  2115.                     '
  2116.                     '' SAVE MAXIMUM VALUE
  2117.                     'if arrAxis(iController, iLoop) > arrAxisMax(iController, iLoop) then
  2118.                     '    arrAxisMax(iController, iLoop) = arrAxis(iController, iLoop)
  2119.                     'end if
  2120.                     '
  2121.                     '' ADD CURRENT VALUE TO AVERAGE SUM
  2122.                     'arrAxisAvg(iController, iLoop) = arrAxisAvg(iController, iLoop) + arrAxis(iController, iLoop)
  2123.                    
  2124.                 NEXT iLoop
  2125.             WEND ' clear and update the device buffer
  2126.  
  2127.         NEXT iController
  2128.        
  2129.         'PRINT "*** iNumControllers=" + cstr$(iNumControllers) + " ***"
  2130.         'iMeasureCount = iMeasureCount + 1
  2131.         'if iMeasureCount = 10 then
  2132.             'iMeasureCount = 0
  2133.            
  2134.             ' And below here is just the simple display routine which displays our values.
  2135.             ' If this was for a game, I'd choose something like arrAxis(1) = -1 for a left arrow style input,
  2136.             ' arrAxis(1) = 1 for a right arrow style input, rather than just using _KEYHIT or INKEY$.
  2137.            
  2138.             InitColumns iColCount
  2139.             m_StartRow = 6
  2140.             m_EndRow = iRows - 2
  2141.             'm_StartCol
  2142.             'm_EndCol
  2143.            
  2144.             CLS
  2145.             PrintString 1, 1, "Game controller test program."
  2146.             PrintString 1, 2, "This program is free to use and distribute per GNU GPLv3 license."
  2147.             PrintString 1, 3, "Tests up to 4 controllers with 2 axes / 2 buttons each."
  2148.             PrintString 1, 4, "Plug in controllers and move them & press buttons."
  2149.             PrintString 1, 5, "-------------------------------------------------------------------------------"
  2150.            
  2151.             iGroupCount = 0
  2152.            
  2153.             FOR iController = 1 TO iNumControllers
  2154.                 FOR iLoop = 1 TO arrController(iController).axisCount ' A loop for each axis
  2155.                     strAxis = right$("  " + cstr$(iLoop), 2)
  2156.                    
  2157.                     sLine = ""
  2158.                    
  2159.                     ' display their status to the screen
  2160.                     sLine = sLine + "Player " + cstr$(iController)
  2161.                    
  2162.                     strValue = FormatNumber$(arrAxis(iController, iLoop), iDigits)
  2163.                     sLine = sLine + ",   Axis #" + strAxis + " = " + strValue
  2164.                    
  2165.                     'strValue = FormatNumber$(arrAxisMin(iController, iLoop), iDigits)
  2166.                     'sLine = sLine + ", Min=" + strValue
  2167.                     '
  2168.                     'strValue = FormatNumber$(arrAxisMax(iController, iLoop), iDigits)
  2169.                     'sLine = sLine + ", Max=" + strValue
  2170.                     '
  2171.                     '' COMPUTE AVERAGE
  2172.                     'dblAverage = arrAxisAvg(iController, iLoop) / 10
  2173.                     'dblAverage = RoundUpDouble# (dblAverage, 3)
  2174.                     'strValue = FormatNumber$(dblAverage, iDigits)
  2175.                     'sLine = sLine + ", Avg=" + strValue
  2176.                     '
  2177.                     '' CLEAR THE AVERAGE
  2178.                     'arrAxisAvg(iController, iLoop) = 0
  2179.                    
  2180.                     PrintColumn sLine
  2181.                 NEXT iLoop
  2182.                 FOR iLoop = 1 TO arrController(iController).buttonCount ' A loop for each button
  2183.                     strAxis = right$("  " + cstr$(iLoop), 2)
  2184.                    
  2185.                     sLine = ""
  2186.                    
  2187.                     ' display their status to the screen
  2188.                     sLine = sLine + "Player " + cstr$(iController)
  2189.                    
  2190.                     strValue = FormatNumber$(arrButton(iController, iLoop), iDigits)
  2191.                     sLine = sLine + ", Button #" + strAxis + " = " + strValue
  2192.                    
  2193.                     'strValue = FormatNumber$(arrButtonMin(iController, iLoop), iDigits)
  2194.                     'sLine = sLine + ", Min=" + strValue
  2195.                     '
  2196.                     'strValue = FormatNumber$(arrButtonMax(iController, iLoop), iDigits)
  2197.                     'sLine = sLine + ", Max=" + strValue
  2198.                    
  2199.                     PrintColumn sLine
  2200.                 NEXT iLoop
  2201.                
  2202.                 iGroupCount = iGroupCount + 1
  2203.                 if iGroupCount = 2 then
  2204.                     ColumnBreak
  2205.                     iGroupCount = 0
  2206.                 end if
  2207.                
  2208.             NEXT iController
  2209.            
  2210.             PrintString 1, iRows - 1, "-------------------------------------------------------------------------------"
  2211.             PrintString 1, iRows - 0, "PRESS <ESC> TO EXIT"
  2212.            
  2213.         'end if
  2214.        
  2215.         _LIMIT 30
  2216.     LOOP UNTIL _KEYHIT = 27 ' ESCAPE to quit
  2217.    
  2218.     ' RETURN TO TEXT SCREEN
  2219.     Screen 0
  2220. END SUB ' TestJoysticks1
  2221.  
  2222. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2223. ' BEGIN FILE FUNCTIONS
  2224. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2225.  
  2226. ' /////////////////////////////////////////////////////////////////////////////
  2227. ' File format is comma-delimited
  2228. ' containing controller info for one action per line
  2229. ' where each line contains the following in this order:
  2230.  
  2231. ' TAB ORDER   INFO             TYPE      DESCRIPTION
  2232. ' 1           {player #}       Integer   player # this mapping is for
  2233. ' 2           {which action}   Integer   which action this mapping is for (up/down/right/left/button 1/button 2, etc.)
  2234. ' 3           {device #}       Integer   number of the device this is mapped to
  2235. ' 4           {type}           Integer   type of input (one of: cInputKey, cInputButton, cInputAxis)
  2236. ' 5           {which}          Integer   if button the _BUTTON #, if axis the _AXIS #, if keyboard the _BUTTON #
  2237. ' 6           {value}          Integer   if axis, the value (-1 or 1), else can be ignored
  2238.  
  2239. ' These need to be declared globally and populated:
  2240. '     ReDim Shared m_arrControlMap(1 To 8, 1 To 8) As ControlInputType
  2241. '     Dim Shared m_ControlMapFileName$: m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  2242.  
  2243. ' If there is an error, returns error message,
  2244. ' else returns blank string.
  2245.  
  2246. Function SaveControllerMap$
  2247.     Dim RoutineName As String:: RoutineName = "SaveControllerMap$"
  2248.     Dim sResult As String : sResult = ""
  2249.     Dim sError As String: sError = ""
  2250.     Dim sFile As String
  2251.     Dim in$
  2252.     Dim iPlayer As Integer
  2253.     Dim iWhichInput As Integer
  2254.     Dim sLine As String
  2255.     Dim iCount As Long: iCount = 0
  2256.     'Dim iError As Long: iError = 0
  2257.     Dim sDelim As String : sDelim = "," ' CHR$(9)
  2258.    
  2259.     DebugPrint "--------------------------------------------------------------------------------"
  2260.     DebugPrint "Started " + RoutineName
  2261.     DebugPrint "--------------------------------------------------------------------------------"
  2262.    
  2263.     ' Get file name
  2264.     If Len(m_ControlMapFileName$) = 0 Then
  2265.         m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  2266.     End If
  2267.     sFile = Mid$(m_ControlMapFileName$, _InStrRev(m_ControlMapFileName$, "\") + 1)
  2268.    
  2269.     '_KeyClear
  2270.     'Cls
  2271.     'Print "SAVE CONTROLLER MAPPING:"
  2272.     'Print "Default file name is " + Chr$(34) + m_ControlMapFileName$ + Chr$(34) + "."
  2273.     'Input "Type save file name, or blank for default: ", in$
  2274.     'in$ = _Trim$(in$)
  2275.     'If Len(in$) > 0 Then
  2276.     '    m_ControlMapFileName$ = in$
  2277.     'End If
  2278.     'sFile = m_ProgramPath$ + m_ControlMapFileName$
  2279.    
  2280.     DebugPrint "m_ControlMapFileName$=" + CHR$(34) + m_ControlMapFileName$ + CHR$(34)
  2281.    
  2282.     ' Save mapping to file
  2283.     Open m_ControlMapFileName$ For Output As #1
  2284.    
  2285.     For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  2286.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  2287.             sLine = ""
  2288.            
  2289.             sLine = sLine + _Trim$(Str$(iPlayer))
  2290.             sLine = sLine + sDelim
  2291.            
  2292.             sLine = sLine + _Trim$(Str$(iWhichInput))
  2293.             sLine = sLine + sDelim
  2294.            
  2295.             sLine = sLine + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).device))            
  2296.             sLine = sLine + sDelim
  2297.            
  2298.             sLine = sLine + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).typ))
  2299.             sLine = sLine + sDelim
  2300.            
  2301.             sLine = sLine + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).code))
  2302.             sLine = sLine + sDelim
  2303.            
  2304.             sLine = sLine + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).value))
  2305.            
  2306.             Print #1, sLine
  2307.             iCount = iCount + 1
  2308.         Next iWhichInput
  2309.     Next iPlayer
  2310.  
  2311.     Close #1
  2312.  
  2313.     DebugPrint "Wrote   " + _Trim$(Str$(iCount)) + " lines."
  2314.     'Print "Skipped " + _Trim$(Str$(iError)) + " lines."
  2315.     DebugPrint ""
  2316.     'Input "PRESS <ENTER> TO CONTINUE", in$
  2317.    
  2318.     If len(sError) = 0 then
  2319.         sResult = "Saved mapping file " + chr$(34) + sFile + chr$(34) + "."
  2320.     Else
  2321.         sResult = "ERRORS: " + sError
  2322.     End If
  2323.    
  2324.     SaveControllerMap$ = sResult
  2325. End Function ' SaveControllerMap$
  2326.  
  2327. ' /////////////////////////////////////////////////////////////////////////////
  2328.  
  2329. Function LoadControllerMap$
  2330.     Dim RoutineName As String:: RoutineName = "LoadControllerMap$"
  2331.     Dim sResult As String: sResult = ""
  2332.     Dim sError As String: sError = ""
  2333.     Dim sNextErr As String
  2334.    
  2335.     Dim sFile As String
  2336.     Dim sText As String
  2337.     Dim iTotal As Long: iTotal = 0
  2338.    
  2339.     Dim iRead As Long: iRead = 0
  2340.     Dim iValid As Long : iValid = 0
  2341.     Dim iBad As Long : iBad = 0
  2342.     Dim iBlank As Long : iBlank = 0
  2343.    
  2344.     Dim sLine As String
  2345.     ReDim arrNextLine(-1) As String
  2346.     Dim iNumValues As Integer
  2347.     Dim iAdjust As Integer
  2348.    
  2349.     Dim iPlayer As Integer
  2350.     Dim iWhichInput As Integer
  2351.     Dim iDevice As Integer
  2352.     Dim iType As Integer
  2353.     Dim iWhich As Integer
  2354.     Dim iValue As Integer
  2355.    
  2356.     'Dim sDebugLine As String
  2357.    
  2358.     DebugPrint "--------------------------------------------------------------------------------"
  2359.     DebugPrint "Started " + RoutineName
  2360.     DebugPrint "--------------------------------------------------------------------------------"
  2361.    
  2362.     ' Get file name
  2363.     If Len(sError) = 0 Then
  2364.         If Len(m_ControlMapFileName$) = 0 Then
  2365.             m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  2366.         End If
  2367.         sFile = Mid$(m_ControlMapFileName$, _InStrRev(m_ControlMapFileName$, "\") + 1)
  2368.     End If
  2369.    
  2370.     '' Get file name
  2371.     'If Len(sError) = 0 Then
  2372.     '    Cls
  2373.     '    If Len(m_ControlMapFileName$) = 0 Then
  2374.     '        m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  2375.     '    End If
  2376.     '    Print "LOAD CONTROLLER MAPPING:"
  2377.     '    Print "Default file name is " + Chr$(34) + m_ControlMapFileName$ + Chr$(34) + "."
  2378.     '    Input "Type name of file to open, or blank for default: ", in$
  2379.     '    in$ = _Trim$(in$)
  2380.     '    If Len(in$) > 0 Then
  2381.     '        m_ControlMapFileName$ = in$
  2382.     '    End If
  2383.     '    sFile = m_ProgramPath$ + m_ControlMapFileName$
  2384.     'End If
  2385.    
  2386.     ' Make sure file exists
  2387.     If Len(sError) = 0 Then
  2388.         If _FileExists(m_ControlMapFileName$) = FALSE Then
  2389.             sError = "File not found: " + Chr$(34) + m_ControlMapFileName$ + Chr$(34)
  2390.         Else
  2391.             DebugPrint "Found file: " + chr$(34) + m_ControlMapFileName$ + chr$(34)
  2392.         End If
  2393.     End If
  2394.    
  2395.     ' Read data from file
  2396.     If Len(sError) = 0 Then
  2397.         DebugPrint "OPEN m_ControlMapFileName$ FOR BINARY AS #1"
  2398.        
  2399.         Open m_ControlMapFileName$ For Binary As #1
  2400.         sText = Space$(LOF(1))
  2401.         Get #1, , sText
  2402.         Close #1
  2403.         iTotal = Len(sText) - Len(Replace$(sText, Chr$(13), ""))
  2404.         sText = ""
  2405.        
  2406.         Open m_ControlMapFileName$ For Input As #1
  2407.         While Not EOF(1)
  2408.             'INPUT #1, sLine
  2409.             Line Input #1, sLine ' read entire text file line
  2410.  
  2411.             iRead = iRead + 1
  2412.             DebugPrint "Parsing line " + _Trim$(Str$(iRead)) + _
  2413.                 " of " + _Trim$(Str$(iTotal))
  2414.            
  2415.             sLine = Replace$(sLine, " ", "") ' Remove spaces
  2416.             sLine = Replace$(sLine, Chr$(9), "") ' Remove tabs
  2417.             sLine = Replace$(sLine, Chr$(10), "") ' Remove line breaks
  2418.             sLine = Replace$(sLine, Chr$(13), "") ' Remove carriage returns
  2419.             DebugPrint "    Trimmed=" + chr$(34) + sLine + chr$(34)
  2420.            
  2421.             If Len(sLine) > 0 Then
  2422.                 split sLine, ",", arrNextLine()
  2423.                 DebugPrint "split into arrNextLine()"
  2424.                 DebugPrint "    lbound =" + _Trim$(Str$(lbound(arrNextLine))) '+ CHR$(10)
  2425.                 DebugPrint "    ubound =" + _Trim$(Str$(ubound(arrNextLine))) '+ CHR$(10)
  2426.                
  2427.                 iNumValues = (ubound(arrNextLine) - lbound(arrNextLine)) + 1
  2428.                 if iNumValues > 5 then
  2429.                     iAdjust = -1 '- lbound(arrNextLine)
  2430.                    
  2431.                     if len(sNextErr) = 0 then
  2432.                         if IsNum%(arrNextLine(1 + iAdjust)) = TRUE then
  2433.                             iPlayer = VAL(arrNextLine(1 + iAdjust))
  2434.                         else
  2435.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 1: not a number"
  2436.                         end if
  2437.                     end if
  2438.                    
  2439.                     if len(sNextErr) = 0 then
  2440.                         if IsNum%(arrNextLine(2 + iAdjust)) = TRUE then
  2441.                             iWhichInput = VAL(arrNextLine(2 + iAdjust))
  2442.                         else
  2443.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 2: not a number"
  2444.                         end if
  2445.                     end if
  2446.                    
  2447.                     if len(sNextErr) = 0 then
  2448.                         if IsNum%(arrNextLine(3 + iAdjust)) = TRUE then
  2449.                             iDevice = VAL(arrNextLine(3 + iAdjust))
  2450.                         else
  2451.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 3: not a number"
  2452.                         end if
  2453.                     end if
  2454.                    
  2455.                     if len(sNextErr) = 0 then
  2456.                         if IsNum%(arrNextLine(4 + iAdjust)) = TRUE then
  2457.                             iType = VAL(arrNextLine(4 + iAdjust))
  2458.                         else
  2459.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 4: not a number"
  2460.                         end if
  2461.                     end if
  2462.                    
  2463.                     if len(sNextErr) = 0 then
  2464.                         if IsNum%(arrNextLine(5 + iAdjust)) = TRUE then
  2465.                             iWhich = VAL(arrNextLine(5 + iAdjust))
  2466.                         else
  2467.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 5: not a number"
  2468.                         end if
  2469.                     end if
  2470.                    
  2471.                     if len(sNextErr) = 0 then
  2472.                         if IsNum%(arrNextLine(6 + iAdjust)) = TRUE then
  2473.                             iValue = VAL(arrNextLine(6 + iAdjust))
  2474.                         else
  2475.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 6: not a number"
  2476.                         end if
  2477.                     end if
  2478.                    
  2479.                     ' validate iPlayer
  2480.                     if len(sNextErr) = 0 then
  2481.                         if iPlayer < lbound(m_arrControlMap, 1) then
  2482.                             sNextErr = "Player value " + _Trim$(Str$(iPlayer)) + _
  2483.                                 " is outside lbound(m_arrControlMap, 1) " + _
  2484.                                 " which is " + _Trim$(Str$(lbound(m_arrControlMap, 1))) + "."
  2485.                         elseif iPlayer > ubound(m_arrControlMap, 1) then
  2486.                             sNextErr = "Player value " + _Trim$(Str$(iPlayer)) + _
  2487.                                 " is outside ubound(m_arrControlMap, 1) " + _
  2488.                                 " which is " + _Trim$(Str$(ubound(m_arrControlMap, 1))) + "."
  2489.                         end if
  2490.                     end if
  2491.                    
  2492.                     ' validate iWhichInput
  2493.                     if len(sNextErr) = 0 then
  2494.                         if iWhichInput < lbound(m_arrControlMap, 2) then
  2495.                             sNextErr = "WhichInput value " + _Trim$(Str$(iWhichInput)) + _
  2496.                                 " is outside lbound(m_arrControlMap, 2) " + _
  2497.                                 " which is " + _Trim$(Str$(lbound(m_arrControlMap, 2))) + "."
  2498.                         elseif iWhichInput > ubound(m_arrControlMap, 2) then
  2499.                             sNextErr = "WhichInput value " + _Trim$(Str$(iWhichInput)) + _
  2500.                                 " is outside ubound(m_arrControlMap, 2) " + _
  2501.                                 " which is " + _Trim$(Str$(ubound(m_arrControlMap, 2))) + "."
  2502.                         end if
  2503.                     end if
  2504.                    
  2505.                 else
  2506.                     sNextErr = "Error on line " + cstr$(iRead) + ": detected " + cstr$(iNumValues) + " values, expected 6."
  2507.                 end if
  2508.                
  2509.                 if len(sNextErr) = 0 then
  2510.                     iValid = iValid + 1
  2511.                     m_arrControlMap(iPlayer, iWhichInput).device = iDevice
  2512.                     m_arrControlMap(iPlayer, iWhichInput).typ = iType
  2513.                     m_arrControlMap(iPlayer, iWhichInput).code = iWhich
  2514.                     m_arrControlMap(iPlayer, iWhichInput).value = iValue
  2515.                 else
  2516.                     iBad = iBad + 1
  2517.                     DebugPrint sNextErr
  2518.                 end if
  2519.             Else
  2520.                 DebugPrint "    Line is blank: skipped"
  2521.                 iBlank = iBlank + 1
  2522.             End If ' LEN(sLine) > 0
  2523.  
  2524.         Wend
  2525.         Close #1
  2526.     End If
  2527.    
  2528.     DebugPrint ""
  2529.     DebugPrint "Lines read: " + _Trim$(Str$(iRead))
  2530.     DebugPrint "Valid     : " + _Trim$(Str$(iValid))
  2531.     DebugPrint "Invalid   : " + _Trim$(Str$(iErrors))
  2532.     DebugPrint "Blank     : " + _Trim$(Str$(iBlank))
  2533.     DebugPrint ""
  2534.     'Input "PRESS <ENTER> TO CONTINUE", in$
  2535.    
  2536.     If len(sError) = 0 then
  2537.         sResult = "Loaded mapping file " + chr$(34) + sFile + chr$(34) + "."
  2538.     Else
  2539.         sResult = "ERRORS: " + sError
  2540.     End If
  2541.    
  2542.     LoadControllerMap$ = sResult
  2543. End Function ' LoadControllerMap$
  2544.  
  2545. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2546. ' END FILE FUNCTIONS
  2547. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2548.  
  2549. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2550. ' BEGIN GRAPHIC PRINTING ROUTINES
  2551. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2552.  
  2553. ' /////////////////////////////////////////////////////////////////////////////
  2554. ' Eliminates the math.
  2555.  
  2556. ' Text resolution:
  2557. '  648 x  480:  80 x 30
  2558. '  720 x  480:  90 x 30
  2559. '  800 x  600: 100 x 37
  2560. ' 1024 x  768: 128 x 48
  2561. ' 1280 x 1024: 160 x 64
  2562. ' 1920 x 1080: 240 x 67
  2563. ' 2048 x 1152: 256 x 72 (truncated after 70 rows, 255 columns)
  2564. ' 3840 x 2160: 480 x135 (truncated after 133 rows, 479 columns)
  2565.  
  2566. SUB PrintString(iCol As Integer, iRow As Integer, MyString As String)
  2567.     Dim iCols As Integer
  2568.     Dim iRows As Integer
  2569.     Dim iX As Integer
  2570.     Dim iY As Integer
  2571.     iCols = _WIDTH(0) \ _FontWidth
  2572.     iRows = _HEIGHT(0) \ _FontHeight
  2573.     iX = _FontWidth * (iCol - 1)
  2574.     iY = _FontHeight * (iRow - 1)
  2575.     _PRINTSTRING (iX, iY), MyString
  2576. END SUB ' PrintString
  2577.  
  2578. ' /////////////////////////////////////////////////////////////////////////////
  2579. ' A way to automatically print to columns.
  2580.  
  2581. ' Depends on the following shared variables:
  2582. '     Dim Shared m_NumColumns As Integer : m_NumColumns = 1
  2583. '     Dim Shared m_PrintRow As Integer : m_PrintRow = 0
  2584. '     Dim Shared m_PrintCol As Integer : m_PrintCol = 0
  2585. '     Dim Shared m_StartRow As Integer : m_StartRow = 0
  2586. '     Dim Shared m_EndRow As Integer : m_EndRow = 0
  2587. '     Dim Shared m_StartCol As Integer : m_StartCol = 0
  2588. '     Dim Shared m_EndCol As Integer : m_EndCol = 0
  2589.  
  2590. ' InitColumns 2
  2591. ' m_PrintRow = 5
  2592. ' m_PrintCol = 2
  2593. ' PrintColumn "Col 2, Row 5"
  2594. ' PrintColumn "m_NumColumns=" + cstr$(m_NumColumns)
  2595.  
  2596. SUB PrintColumn(MyString As String)
  2597.     Dim iCols As Integer
  2598.     Dim iRows As Integer
  2599.     Dim iX As Integer
  2600.     Dim iY As Integer
  2601.    
  2602.     REDIM arrLines(-1) As String
  2603.     DIM iRow As Integer
  2604.     DIM iCol As Integer
  2605.     DIM sChar As String
  2606.     Dim sLine As String
  2607.     Dim iColWidth As Integer
  2608.    
  2609.     iCols = _WIDTH(0) \ _FontWidth
  2610.     iRows = _HEIGHT(0) \ _FontHeight
  2611.    
  2612.     if m_NumColumns < 1 or m_NumColumns > iCols then
  2613.         m_NumColumns = 1
  2614.     end if
  2615.    
  2616.     if m_StartRow < 1 or m_StartRow > iRows then
  2617.         m_StartRow = 1
  2618.     end if
  2619.     if m_EndRow < m_StartRow or m_EndRow > iRows then
  2620.         m_EndRow = iRows
  2621.     end if
  2622.     if m_StartCol < 1 or m_StartCol > m_NumColumns then
  2623.         m_StartCol = 1
  2624.     end if
  2625.     if m_EndCol < m_StartCol or m_EndCol > m_NumColumns then
  2626.         m_EndCol = m_NumColumns
  2627.     end if
  2628.    
  2629.     if m_PrintRow < m_StartRow then
  2630.         m_PrintRow = m_StartRow
  2631.     end if
  2632.     if m_PrintCol < m_StartCol then
  2633.         m_PrintCol = m_StartCol
  2634.     end if
  2635.    
  2636.     iColWidth = iCols \ m_NumColumns
  2637.    
  2638.     IF m_PrintRow <= m_EndRow THEN
  2639.         IF m_PrintCol <= m_EndCol THEN
  2640.             split MyString, chr$(13), arrLines()
  2641.             FOR iRow = 0 TO UBOUND(arrlines)
  2642.                 sLine = left$(arrLines(iRow), iColWidth)
  2643.                 'TODO: wrap remaining text
  2644.                 iX = _FontWidth * ((m_PrintCol - 1) * iColWidth)
  2645.                 iY = _FontHeight * (m_PrintRow - 1)
  2646.                 _PRINTSTRING (iX, iY), sLine
  2647.                
  2648.                 m_PrintRow = m_PrintRow + 1
  2649.                 IF m_PrintRow > m_EndRow THEN
  2650.                     m_PrintRow = m_StartRow
  2651.                     m_PrintCol = m_PrintCol + 1
  2652.                     if m_PrintCol > m_NumColumns then
  2653.                         'TODO: options for when we reach the bottom of the last column (stop printing, wrap around)
  2654.                         m_PrintCol = 1
  2655.                     end if
  2656.                 END IF
  2657.             NEXT iRow
  2658.         END IF
  2659.     END IF
  2660. END SUB ' PrintColumn
  2661.  
  2662. ' /////////////////////////////////////////////////////////////////////////////
  2663. ' A way to automatically print to columns.
  2664.  
  2665. SUB ColumnBreak()
  2666.     m_PrintRow = m_StartRow
  2667.     m_PrintCol = m_PrintCol + 1
  2668.     if m_PrintCol > m_NumColumns then
  2669.         'TODO: options for when we go past the last column (stop printing, wrap around)
  2670.     end if
  2671. END SUB ' ColumnBreak
  2672.  
  2673. ' /////////////////////////////////////////////////////////////////////////////
  2674. ' A way to automatically print to columns.
  2675.  
  2676. SUB InitColumns(iNumColumns As Integer)
  2677.     Dim iCols As Integer
  2678.     Dim iRows As Integer
  2679.     iCols = _WIDTH(0) \ _FontWidth
  2680.     iRows = _HEIGHT(0) \ _FontHeight
  2681.     if iNumColumns < 1 or iNumColumns > iCols then
  2682.         m_NumColumns = 1
  2683.     else
  2684.         m_NumColumns = iNumColumns
  2685.     end if
  2686.    
  2687.     if m_StartRow < 1 or m_StartRow > iRows then
  2688.         m_StartRow = 1
  2689.     end if
  2690.     if m_EndRow < m_StartRow or m_EndRow > iRows then
  2691.         m_EndRow = iRows
  2692.     end if
  2693.     if m_StartCol < 1 or m_StartCol > m_NumColumns then
  2694.         m_StartCol = 1
  2695.     end if
  2696.     if m_EndCol < m_StartCol or m_EndCol > m_NumColumns then
  2697.         m_EndCol = m_NumColumns
  2698.     end if
  2699.    
  2700.     m_PrintRow = 1
  2701.     m_PrintCol = 1
  2702. END SUB ' InitColumns
  2703.  
  2704. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2705. ' END GRAPHIC PRINTING ROUTINES
  2706. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2707.  
  2708. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2709. ' BEGIN DEBUGGING ROUTINES #DEBUGGING
  2710. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2711. sub DebugPrint(s$)
  2712.     IF m_bTesting = TRUE THEN
  2713.         _echo s$
  2714.         'ReDim arrLines$(0)
  2715.         'dim delim$ : delim$ = Chr$(13)
  2716.         'split MyString, delim$, arrLines$()
  2717.     END IF
  2718. end sub ' DebugPrint
  2719.  
  2720. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2721. ' END DEBUGGING ROUTINES @DEBUGGING
  2722. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2723.  
  2724. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2725. ' BEGIN GENERAL PURPOSE ROUTINES #GEN
  2726. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2727.  
  2728. ' /////////////////////////////////////////////////////////////////////////////
  2729. ' Convert a value to string and trim it (because normal Str$ adds spaces)
  2730.  
  2731. FUNCTION cstr$ (myValue)
  2732.     'cstr$ = LTRIM$(RTRIM$(STR$(myValue)))
  2733.     cstr$ = _TRIM$(STR$(myValue))
  2734. END FUNCTION ' cstr$
  2735.  
  2736. ' /////////////////////////////////////////////////////////////////////////////
  2737. ' Convert a Long value to string and trim it (because normal Str$ adds spaces)
  2738.  
  2739. FUNCTION cstrl$ (myValue AS LONG)
  2740.     cstrl$ = _TRIM$(STR$(myValue))
  2741. END FUNCTION ' cstrl$
  2742.  
  2743. ' /////////////////////////////////////////////////////////////////////////////
  2744. ' Convert a Single value to string and trim it (because normal Str$ adds spaces)
  2745.  
  2746. Function cstrs$ (myValue As Single)
  2747.     ''cstr$ = LTRIM$(RTRIM$(STR$(myValue)))
  2748.     cstrs$ = _Trim$(Str$(myValue))
  2749. End Function ' cstrs$
  2750.  
  2751. ' /////////////////////////////////////////////////////////////////////////////
  2752. ' Convert an unsigned Long value to string and trim it (because normal Str$ adds spaces)
  2753.  
  2754. Function cstrul$ (myValue As _Unsigned Long)
  2755.     cstrul$ = _Trim$(Str$(myValue))
  2756. End Function ' cstrul$
  2757.  
  2758. ' /////////////////////////////////////////////////////////////////////////////
  2759. ' Scientific notation - QB64 Wiki
  2760. ' https://www.qb64.org/wiki/Scientific_notation
  2761.  
  2762. ' Example: A string function that displays extremely small or large exponential decimal values.
  2763.  
  2764. FUNCTION DblToStr$ (n#)
  2765.     value$ = UCASE$(LTRIM$(STR$(n#)))
  2766.     Xpos% = INSTR(value$, "D") + INSTR(value$, "E")  'only D or E can be present
  2767.     IF Xpos% THEN
  2768.       expo% = VAL(MID$(value$, Xpos% + 1))
  2769.       IF VAL(value$) < 0 THEN
  2770.         sign$ = "-": valu$ = MID$(value$, 2, Xpos% - 2)
  2771.       ELSE valu$ = MID$(value$, 1, Xpos% - 1)
  2772.       END IF
  2773.       dot% = INSTR(valu$, "."): L% = LEN(valu$)
  2774.       IF expo% > 0 THEN add$ = STRING$(expo% - (L% - dot%), "0")
  2775.       IF expo% < 0 THEN min$ = STRING$(ABS(expo%) - (dot% - 1), "0"): DP$ = "."
  2776.       FOR n = 1 TO L%
  2777.         IF MID$(valu$, n, 1) <> "." THEN num$ = num$ + MID$(valu$, n, 1)
  2778.       NEXT
  2779.     ELSE DblToStr$ = value$: EXIT FUNCTION
  2780.     END IF
  2781.     DblToStr$ = _TRIM$( sign$ + DP$ + min$ + num$ + add$ )
  2782. END FUNCTION ' DblToStr$
  2783.  
  2784. ' /////////////////////////////////////////////////////////////////////////////
  2785.  
  2786. FUNCTION FormatNumber$(myValue, iDigits AS INTEGER)
  2787.     DIM strValue AS STRING
  2788.     strValue = DblToStr$(myValue) + STRING$(iDigits, " ")
  2789.     IF myValue < 1 THEN
  2790.         IF myValue < 0 THEN
  2791.             strValue = Replace$(strValue, "-.", "-0.")
  2792.         ELSEIF myValue > 0 THEN
  2793.             strValue = "0" + strValue
  2794.         END IF
  2795.     END IF
  2796.     FormatNumber$ = LEFT$(strValue, iDigits)
  2797. END FUNCTION ' FormatNumber$
  2798.  
  2799. ' /////////////////////////////////////////////////////////////////////////////
  2800. ' From: Bitwise Manipulations By Steven Roman
  2801. ' http://www.romanpress.com/Articles/Bitwise_R/Bitwise.htm
  2802.  
  2803. ' Returns the 8-bit binary representation
  2804. ' of an integer iInput where 0 <= iInput <= 255
  2805.  
  2806. Function GetBinary$ (iInput1 As Integer)
  2807.     Dim sResult As String
  2808.     Dim iLoop As Integer
  2809.     Dim iInput As Integer: iInput = iInput1
  2810.  
  2811.     sResult = ""
  2812.  
  2813.     If iInput >= 0 And iInput <= 255 Then
  2814.         For iLoop = 1 To 8
  2815.             sResult = LTrim$(RTrim$(Str$(iInput Mod 2))) + sResult
  2816.             iInput = iInput \ 2
  2817.             'If iLoop = 4 Then sResult = " " + sResult
  2818.         Next iLoop
  2819.     End If
  2820.  
  2821.     GetBinary$ = sResult
  2822. End Function ' GetBinary$
  2823.  
  2824. ' /////////////////////////////////////////////////////////////////////////////
  2825. ' wonderfully inefficient way to read if a bit is set
  2826. ' ival = GetBit256%(int we are comparing, int containing the bits we want to read)
  2827.  
  2828. ' See also: GetBit256%, SetBit256%
  2829.  
  2830. Function GetBit256% (iNum1 As Integer, iBit1 As Integer)
  2831.     Dim iResult As Integer
  2832.     Dim sNum As String
  2833.     Dim sBit As String
  2834.     Dim iLoop As Integer
  2835.     Dim bContinue As Integer
  2836.     'DIM iTemp AS INTEGER
  2837.     Dim iNum As Integer: iNum = iNum1
  2838.     Dim iBit As Integer: iBit = iBit1
  2839.  
  2840.     iResult = FALSE
  2841.     bContinue = TRUE
  2842.  
  2843.     If iNum < 256 And iBit <= 128 Then
  2844.         sNum = GetBinary$(iNum)
  2845.         sBit = GetBinary$(iBit)
  2846.         For iLoop = 1 To 8
  2847.             If Mid$(sBit, iLoop, 1) = "1" Then
  2848.                 'if any of the bits in iBit are false, return false
  2849.                 If Mid$(sNum, iLoop, 1) = "0" Then
  2850.                     iResult = FALSE
  2851.                     bContinue = FALSE
  2852.                     Exit For
  2853.                 End If
  2854.             End If
  2855.         Next iLoop
  2856.         If bContinue = TRUE Then
  2857.             iResult = TRUE
  2858.         End If
  2859.     End If
  2860.  
  2861.     GetBit256% = iResult
  2862. End Function ' GetBit256%
  2863.  
  2864. ' /////////////////////////////////////////////////////////////////////////////
  2865. ' From: Bitwise Manipulations By Steven Roman
  2866. ' http://www.romanpress.com/Articles/Bitwise_R/Bitwise.htm
  2867.  
  2868. ' Returns the integer that corresponds to a binary string of length 8
  2869.  
  2870. Function GetIntegerFromBinary% (sBinary1 As String)
  2871.     Dim iResult As Integer
  2872.     Dim iLoop As Integer
  2873.     Dim strBinary As String
  2874.     Dim sBinary As String: sBinary = sBinary1
  2875.  
  2876.     iResult = 0
  2877.     strBinary = Replace$(sBinary, " ", "") ' Remove any spaces
  2878.     For iLoop = 0 To Len(strBinary) - 1
  2879.         iResult = iResult + 2 ^ iLoop * Val(Mid$(strBinary, Len(strBinary) - iLoop, 1))
  2880.     Next iLoop
  2881.  
  2882.     GetIntegerFromBinary% = iResult
  2883. End Function ' GetIntegerFromBinary%
  2884.  
  2885. ' /////////////////////////////////////////////////////////////////////////////
  2886.  
  2887. Function IIF (Condition, IfTrue, IfFalse)
  2888.     If Condition Then IIF = IfTrue Else IIF = IfFalse
  2889.  
  2890. ' /////////////////////////////////////////////////////////////////////////////
  2891.  
  2892. Function IIFSTR$ (Condition, IfTrue$, IfFalse$)
  2893.     If Condition Then IIFSTR$ = IfTrue$ Else IIFSTR$ = IfFalse$
  2894.  
  2895. ' /////////////////////////////////////////////////////////////////////////////
  2896. ' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/
  2897.  
  2898. Function IsEven% (n)
  2899.     If n Mod 2 = 0 Then
  2900.         IsEven% = TRUE
  2901.     Else
  2902.         IsEven% = FALSE
  2903.     End If
  2904. End Function ' IsEven%
  2905.  
  2906. ' /////////////////////////////////////////////////////////////////////////////
  2907. ' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/
  2908.  
  2909. Function IsOdd% (n)
  2910.     If n Mod 2 = 1 Then
  2911.         IsOdd% = TRUE
  2912.     Else
  2913.         IsOdd% = FALSE
  2914.     End If
  2915. End Function ' IsOdd%
  2916.  
  2917. ' /////////////////////////////////////////////////////////////////////////////
  2918. ' By sMcNeill from https://www.qb64.org/forum/index.php?topic=896.0
  2919.  
  2920. Function IsNum% (text$)
  2921.     Dim a$
  2922.     Dim b$
  2923.     a$ = _Trim$(text$)
  2924.     b$ = _Trim$(Str$(Val(text$)))
  2925.     If a$ = b$ Then
  2926.         IsNum% = TRUE
  2927.     Else
  2928.         IsNum% = FALSE
  2929.     End If
  2930. End Function ' IsNum%
  2931.  
  2932. ' /////////////////////////////////////////////////////////////////////////////
  2933. ' Re: Does a Is Number function exist in QB64?
  2934. ' https://www.qb64.org/forum/index.php?topic=896.15
  2935.  
  2936. ' MWheatley
  2937. ' « Reply #18 on: January 01, 2019, 11:24:30 AM »
  2938.  
  2939. ' returns 1 if string is an integer, 0 if not
  2940. Function IsNumber (text$)
  2941.     Dim i As Integer
  2942.  
  2943.     IsNumber = 1
  2944.     For i = 1 To Len(text$)
  2945.         If Asc(Mid$(text$, i, 1)) < 45 Or Asc(Mid$(text$, i, 1)) >= 58 Then
  2946.             IsNumber = 0
  2947.             Exit For
  2948.         ElseIf Asc(Mid$(text$, i, 1)) = 47 Then
  2949.             IsNumber = 0
  2950.             Exit For
  2951.         End If
  2952.     Next i
  2953. End Function ' IsNumber
  2954.  
  2955. ' /////////////////////////////////////////////////////////////////////////////
  2956. ' Split and join strings
  2957. ' https://www.qb64.org/forum/index.php?topic=1073.0
  2958.  
  2959. 'Combine all elements of in$() into a single string with delimiter$ separating the elements.
  2960.  
  2961. Function join$ (in$(), delimiter$)
  2962.     Dim result$
  2963.     Dim i As Long
  2964.     result$ = in$(LBound(in$))
  2965.     For i = LBound(in$) + 1 To UBound(in$)
  2966.         result$ = result$ + delimiter$ + in$(i)
  2967.     Next i
  2968.     join$ = result$
  2969. End Function ' join$
  2970.  
  2971. ' /////////////////////////////////////////////////////////////////////////////
  2972. ' ABS was returning strange values with type LONG
  2973. ' so I created this which does not.
  2974.  
  2975. Function LongABS& (lngValue As Long)
  2976.     If Sgn(lngValue) = -1 Then
  2977.         LongABS& = 0 - lngValue
  2978.     Else
  2979.         LongABS& = lngValue
  2980.     End If
  2981. End Function ' LongABS&
  2982.  
  2983. ' /////////////////////////////////////////////////////////////////////////////
  2984. ' Writes sText to a debug file in the EXE folder.
  2985. ' Debug file is named the same thing as the program EXE name with ".txt" at the end.
  2986. ' For example the program "C:\QB64\MyProgram.BAS" running as
  2987. ' "C:\QB64\MyProgram.EXE" would have an output file "C:\QB64\MyProgram.EXE.txt".
  2988. ' If the file doesn't exist, it is created, otherwise it is appended to.
  2989.  
  2990. Sub DebugPrintFile (sText As String)
  2991.     Dim sFileName As String
  2992.     Dim sError As String
  2993.     Dim sOut As String
  2994.  
  2995.     sFileName = ProgramPath$ + ProgramName$ + ".txt"
  2996.     sError = ""
  2997.     If _FileExists(sFileName) = FALSE Then
  2998.         sOut = ""
  2999.         sOut = sOut + "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10)
  3000.         sOut = sOut + "PROGRAM : " + ProgramName$ + Chr$(13) + Chr$(10)
  3001.         sOut = sOut + "RUN DATE: " + CurrentDateTime$ + Chr$(13) + Chr$(10)
  3002.         sOut = sOut + "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10)
  3003.         sError = PrintFile$(sFileName, sOut, FALSE)
  3004.     End If
  3005.     If Len(sError) = 0 Then
  3006.         sError = PrintFile$(sFileName, sText, TRUE)
  3007.     End If
  3008.     If Len(sError) <> 0 Then
  3009.         Print CurrentDateTime$ + " DebugPrintFile FAILED: " + sError
  3010.     End If
  3011. End Sub ' DebugPrintFile
  3012.  
  3013. ' /////////////////////////////////////////////////////////////////////////////
  3014.  
  3015. Function IntPadRight$(iValue As Integer, iWidth As Integer)
  3016.     IntPadRight$ = left$( _TRIM$(STR$(iValue)) + STRING$(iWidth, " "), iWidth)
  3017. End Function ' IntPadRight$
  3018.  
  3019. ' /////////////////////////////////////////////////////////////////////////////
  3020. ' Returns blank if successful else returns error message.
  3021.  
  3022. Function PrintFile$ (sFileName As String, sText As String, bAppend As Integer)
  3023.     'x = 1: y = 2: z$ = "Three"
  3024.  
  3025.     Dim sError As String: sError = ""
  3026.  
  3027.     If Len(sError) = 0 Then
  3028.         If (bAppend = TRUE) Then
  3029.             If _FileExists(sFileName) Then
  3030.                 Open sFileName For Append As #1 ' opens an existing file for appending
  3031.             Else
  3032.                 sError = "Error in PrintFile$ : File not found. Cannot append."
  3033.             End If
  3034.         Else
  3035.             Open sFileName For Output As #1 ' opens and clears an existing file or creates new empty file
  3036.         End If
  3037.     End If
  3038.     If Len(sError) = 0 Then
  3039.         ' WRITE places text in quotes in the file
  3040.         'WRITE #1, x, y, z$
  3041.         'WRITE #1, sText
  3042.  
  3043.         ' PRINT does not put text inside quotes
  3044.         Print #1, sText
  3045.  
  3046.         Close #1
  3047.  
  3048.         'PRINT "File created with data. Press a key!"
  3049.         'K$ = INPUT$(1) 'press a key
  3050.  
  3051.         'OPEN sFileName FOR INPUT AS #2 ' opens a file to read it
  3052.         'INPUT #2, a, b, c$
  3053.         'CLOSE #2
  3054.  
  3055.         'PRINT a, b, c$
  3056.         'WRITE a, b, c$
  3057.     End If
  3058.  
  3059.     PrintFile$ = sError
  3060. End Function ' PrintFile$
  3061.  
  3062. ' /////////////////////////////////////////////////////////////////////////////
  3063. ' Generate random value between Min and Max.
  3064. Function RandomNumber% (Min%, Max%)
  3065.     Dim NumSpread%
  3066.  
  3067.     ' SET RANDOM SEED
  3068.     'Randomize ' Initialize random-number generator.
  3069.  
  3070.     ' GET RANDOM # Min%-Max%
  3071.     'RandomNumber = Int((Max * Rnd) + Min) ' generate number
  3072.  
  3073.     NumSpread% = (Max% - Min%) + 1
  3074.  
  3075.     RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
  3076.  
  3077. End Function ' RandomNumber%
  3078.  
  3079. ' /////////////////////////////////////////////////////////////////////////////
  3080.  
  3081. Sub RandomNumberTest
  3082.     Dim iCols As Integer: iCols = 10
  3083.     Dim iRows As Integer: iRows = 20
  3084.     Dim iLoop As Integer
  3085.     Dim iX As Integer
  3086.     Dim iY As Integer
  3087.     Dim sError As String
  3088.     Dim sFileName As String
  3089.     Dim sText As String
  3090.     Dim bAppend As Integer
  3091.     Dim iMin As Integer
  3092.     Dim iMax As Integer
  3093.     Dim iNum As Integer
  3094.     Dim iErrorCount As Integer
  3095.     Dim sInput$
  3096.  
  3097.     sFileName = "c:\temp\maze_test_1.txt"
  3098.     sText = "Count" + Chr$(9) + "Min" + Chr$(9) + "Max" + Chr$(9) + "Random"
  3099.     bAppend = FALSE
  3100.     sError = PrintFile$(sFileName, sText, bAppend)
  3101.     If Len(sError) = 0 Then
  3102.         bAppend = TRUE
  3103.         iErrorCount = 0
  3104.  
  3105.         iMin = 0
  3106.         iMax = iCols - 1
  3107.         For iLoop = 1 To 100
  3108.             iNum = RandomNumber%(iMin, iMax)
  3109.             sText = Str$(iLoop) + Chr$(9) + Str$(iMin) + Chr$(9) + Str$(iMax) + Chr$(9) + Str$(iNum)
  3110.             sError = PrintFile$(sFileName, sText, bAppend)
  3111.             If Len(sError) > 0 Then
  3112.                 iErrorCount = iErrorCount + 1
  3113.                 Print Str$(iLoop) + ". ERROR"
  3114.                 Print "    " + "iMin=" + Str$(iMin)
  3115.                 Print "    " + "iMax=" + Str$(iMax)
  3116.                 Print "    " + "iNum=" + Str$(iNum)
  3117.                 Print "    " + "Could not write to file " + Chr$(34) + sFileName + Chr$(34) + "."
  3118.                 Print "    " + sError
  3119.             End If
  3120.         Next iLoop
  3121.  
  3122.         iMin = 0
  3123.         iMax = iRows - 1
  3124.         For iLoop = 1 To 100
  3125.             iNum = RandomNumber%(iMin, iMax)
  3126.             sText = Str$(iLoop) + Chr$(9) + Str$(iMin) + Chr$(9) + Str$(iMax) + Chr$(9) + Str$(iNum)
  3127.             sError = PrintFile$(sFileName, sText, bAppend)
  3128.             If Len(sError) > 0 Then
  3129.                 iErrorCount = iErrorCount + 1
  3130.                 Print Str$(iLoop) + ". ERROR"
  3131.                 Print "    " + "iMin=" + Str$(iMin)
  3132.                 Print "    " + "iMax=" + Str$(iMax)
  3133.                 Print "    " + "iNum=" + Str$(iNum)
  3134.                 Print "    " + "Could not write to file " + Chr$(34) + sFileName + Chr$(34) + "."
  3135.                 Print "    " + sError
  3136.             End If
  3137.         Next iLoop
  3138.  
  3139.         Print "Finished generating numbers. Errors: " + Str$(iErrorCount)
  3140.     Else
  3141.         Print "Error creating file " + Chr$(34) + sFileName + Chr$(34) + "."
  3142.         Print sError
  3143.     End If
  3144.  
  3145.     Input "Press <ENTER> to continue", sInput$
  3146. End Sub ' RandomNumberTest
  3147.  
  3148. ' /////////////////////////////////////////////////////////////////////////////
  3149. ' FROM: String Manipulation
  3150. ' found at abandoned, outdated and now likely malicious qb64 dot net website
  3151. ' http://www.qb64.[net]/forum/index_topic_5964-0/
  3152. '
  3153. 'SUMMARY:
  3154. '   Purpose:  A library of custom functions that transform strings.
  3155. '   Author:   Dustinian Camburides (dustinian@gmail.com)
  3156. '   Platform: QB64 (www.qb64.org)
  3157. '   Revision: 1.6
  3158. '   Updated:  5/28/2012
  3159.  
  3160. 'SUMMARY:
  3161. '[Replace$] replaces all instances of the [Find] sub-string with the [Add] sub-string within the [Text] string.
  3162. 'INPUT:
  3163. 'Text: The input string; the text that's being manipulated.
  3164. 'Find: The specified sub-string; the string sought within the [Text] string.
  3165. 'Add: The sub-string that's being added to the [Text] string.
  3166.  
  3167. Function Replace$ (Text1 As String, Find1 As String, Add1 As String)
  3168.     ' VARIABLES:
  3169.     Dim Text2 As String
  3170.     Dim Find2 As String
  3171.     Dim Add2 As String
  3172.     Dim lngLocation As Long ' The address of the [Find] substring within the [Text] string.
  3173.     Dim strBefore As String ' The characters before the string to be replaced.
  3174.     Dim strAfter As String ' The characters after the string to be replaced.
  3175.  
  3176.     ' INITIALIZE:
  3177.     ' MAKE COPIESSO THE ORIGINAL IS NOT MODIFIED (LIKE ByVal IN VBA)
  3178.     Text2 = Text1
  3179.     Find2 = Find1
  3180.     Add2 = Add1
  3181.  
  3182.     lngLocation = InStr(1, Text2, Find2)
  3183.  
  3184.     ' PROCESSING:
  3185.     ' While [Find2] appears in [Text2]...
  3186.     While lngLocation
  3187.         ' Extract all Text2 before the [Find2] substring:
  3188.         strBefore = Left$(Text2, lngLocation - 1)
  3189.  
  3190.         ' Extract all text after the [Find2] substring:
  3191.         strAfter = Right$(Text2, ((Len(Text2) - (lngLocation + Len(Find2) - 1))))
  3192.  
  3193.         ' Return the substring:
  3194.         Text2 = strBefore + Add2 + strAfter
  3195.  
  3196.         ' Locate the next instance of [Find2]:
  3197.         lngLocation = InStr(1, Text2, Find2)
  3198.  
  3199.         ' Next instance of [Find2]...
  3200.     Wend
  3201.  
  3202.     ' OUTPUT:
  3203.     Replace$ = Text2
  3204. End Function ' Replace$
  3205.  
  3206. ' /////////////////////////////////////////////////////////////////////////////
  3207.  
  3208. Sub ReplaceTest
  3209.     Dim in$
  3210.  
  3211.     Print "-------------------------------------------------------------------------------"
  3212.     Print "ReplaceTest"
  3213.     Print
  3214.  
  3215.     Print "Original value"
  3216.     in$ = "Thiz iz a teZt."
  3217.     Print "in$ = " + Chr$(34) + in$ + Chr$(34)
  3218.     Print
  3219.  
  3220.     Print "Replacing lowercase " + Chr$(34) + "z" + Chr$(34) + " with " + Chr$(34) + "s" + Chr$(34) + "..."
  3221.     in$ = Replace$(in$, "z", "s")
  3222.     Print "in$ = " + Chr$(34) + in$ + Chr$(34)
  3223.     Print
  3224.  
  3225.     Print "Replacing uppercase " + Chr$(34) + "Z" + Chr$(34) + " with " + Chr$(34) + "s" + Chr$(34) + "..."
  3226.     in$ = Replace$(in$, "Z", "s")
  3227.     Print "in$ = " + Chr$(34) + in$ + Chr$(34)
  3228.     Print
  3229.  
  3230.     Print "ReplaceTest finished."
  3231. End Sub ' ReplaceTest
  3232.  
  3233. ' /////////////////////////////////////////////////////////////////////////////
  3234. ' https://www.qb64.org/forum/index.php?topic=3605.0
  3235. ' Quote from: SMcNeill on Today at 03:53:48 PM
  3236. '
  3237. ' Sometimes, you guys make things entirely too  complicated.
  3238. ' There ya go!  Three functions to either round naturally,
  3239. ' always round down, or always round up, to whatever number of digits you desire.
  3240. ' EDIT:  Modified to add another option to round scientific,
  3241. ' since you had it's description included in your example.
  3242.  
  3243. FUNCTION Round## (num##, digits%)
  3244.     Round## = INT(num## * 10 ^ digits% + .5) / 10 ^ digits%
  3245.  
  3246. FUNCTION RoundUp## (num##, digits%)
  3247.     RoundUp## = _CEIL(num## * 10 ^ digits%) / 10 ^ digits%
  3248.  
  3249. FUNCTION RoundDown## (num##, digits%)
  3250.     RoundDown## = INT(num## * 10 ^ digits%) / 10 ^ digits%
  3251.  
  3252. FUNCTION Round_Scientific## (num##, digits%)
  3253.     Round_Scientific## = _ROUND(num## * 10 ^ digits%) / 10 ^ digits%
  3254.  
  3255. FUNCTION RoundUpDouble# (num#, digits%)
  3256.     RoundUpDouble# = _CEIL(num# * 10 ^ digits%) / 10 ^ digits%
  3257.  
  3258. FUNCTION RoundUpSingle! (num!, digits%)
  3259.     RoundUpSingle! = _CEIL(num! * 10 ^ digits%) / 10 ^ digits%
  3260.  
  3261. ' /////////////////////////////////////////////////////////////////////////////
  3262. ' fantastically inefficient way to set a bit
  3263.  
  3264. ' example use: arrMaze(iX, iY) = SetBit256%(arrMaze(iX, iY), cS, FALSE)
  3265.  
  3266. ' See also: GetBit256%, SetBit256%
  3267.  
  3268. ' newint=SetBit256%(oldint, int containing the bits we want to set, value to set them to)
  3269. Function SetBit256% (iNum1 As Integer, iBit1 As Integer, bVal1 As Integer)
  3270.     Dim sNum As String
  3271.     Dim sBit As String
  3272.     Dim sVal As String
  3273.     Dim iLoop As Integer
  3274.     Dim strResult As String
  3275.     Dim iResult As Integer
  3276.     Dim iNum As Integer: iNum = iNum1
  3277.     Dim iBit As Integer: iBit = iBit1
  3278.     Dim bVal As Integer: bVal = bVal1
  3279.  
  3280.     If iNum < 256 And iBit <= 128 Then
  3281.         sNum = GetBinary$(iNum)
  3282.         sBit = GetBinary$(iBit)
  3283.         If bVal = TRUE Then
  3284.             sVal = "1"
  3285.         Else
  3286.             sVal = "0"
  3287.         End If
  3288.         strResult = ""
  3289.         For iLoop = 1 To 8
  3290.             If Mid$(sBit, iLoop, 1) = "1" Then
  3291.                 strResult = strResult + sVal
  3292.             Else
  3293.                 strResult = strResult + Mid$(sNum, iLoop, 1)
  3294.             End If
  3295.         Next iLoop
  3296.         iResult = GetIntegerFromBinary%(strResult)
  3297.     Else
  3298.         iResult = iNum
  3299.     End If
  3300.  
  3301.     SetBit256% = iResult
  3302. End Function ' SetBit256%
  3303.  
  3304. ' /////////////////////////////////////////////////////////////////////////////
  3305. ' Scientific notation - QB64 Wiki
  3306. ' https://www.qb64.org/wiki/Scientific_notation
  3307.  
  3308. ' Example: A string function that displays extremely small or large exponential decimal values.
  3309.  
  3310. FUNCTION SngToStr$ (n!)
  3311.     value$ = UCASE$(LTRIM$(STR$(n!)))
  3312.     Xpos% = INSTR(value$, "D") + INSTR(value$, "E")  'only D or E can be present
  3313.     IF Xpos% THEN
  3314.       expo% = VAL(MID$(value$, Xpos% + 1))
  3315.       IF VAL(value$) < 0 THEN
  3316.         sign$ = "-": valu$ = MID$(value$, 2, Xpos% - 2)
  3317.       ELSE valu$ = MID$(value$, 1, Xpos% - 1)
  3318.       END IF
  3319.       dot% = INSTR(valu$, "."): L% = LEN(valu$)
  3320.       IF expo% > 0 THEN add$ = STRING$(expo% - (L% - dot%), "0")
  3321.       IF expo% < 0 THEN min$ = STRING$(ABS(expo%) - (dot% - 1), "0"): DP$ = "."
  3322.       FOR n = 1 TO L%
  3323.         IF MID$(valu$, n, 1) <> "." THEN num$ = num$ + MID$(valu$, n, 1)
  3324.       NEXT
  3325.     ELSE SngToStr$ = value$: EXIT FUNCTION
  3326.     END IF
  3327.     SngToStr$ = _TRIM$( sign$ + DP$ + min$ + num$ + add$ )
  3328. END FUNCTION ' SngToStr$
  3329.  
  3330. ' /////////////////////////////////////////////////////////////////////////////
  3331. ' Split and join strings
  3332. ' https://www.qb64.org/forum/index.php?topic=1073.0
  3333. '
  3334. ' FROM luke, QB64 Developer
  3335. ' Date: February 15, 2019, 04:11:07 AM »
  3336. '
  3337. ' Given a string of words separated by spaces (or any other character),
  3338. ' splits it into an array of the words. I've no doubt many people have
  3339. ' written a version of this over the years and no doubt there's a million
  3340. ' ways to do it, but I thought I'd put mine here so we have at least one
  3341. ' version. There's also a join function that does the opposite
  3342. ' array -> single string.
  3343. '
  3344. ' Code is hopefully reasonably self explanatory with comments and a little demo.
  3345. ' Note, this is akin to Python/JavaScript split/join, PHP explode/implode.
  3346.  
  3347. 'Split in$ into pieces, chopping at every occurrence of delimiter$. Multiple consecutive occurrences
  3348. 'of delimiter$ are treated as a single instance. The chopped pieces are stored in result$().
  3349. '
  3350. 'delimiter$ must be one character long.
  3351. 'result$() must have been REDIMmed previously.
  3352.  
  3353. ' Modified to handle multi-character delimiters
  3354.  
  3355. Sub split (in$, delimiter$, result$())
  3356.     Dim start As Integer
  3357.     Dim finish As Integer
  3358.     Dim iDelimLen As Integer
  3359.     ReDim result$(-1)
  3360.  
  3361.     iDelimLen = Len(delimiter$)
  3362.  
  3363.     start = 1
  3364.     Do
  3365.         'While Mid$(in$, start, 1) = delimiter$
  3366.         While Mid$(in$, start, iDelimLen) = delimiter$
  3367.             'start = start + 1
  3368.             start = start + iDelimLen
  3369.             If start > Len(in$) Then
  3370.                 Exit Sub
  3371.             End If
  3372.         Wend
  3373.         finish = InStr(start, in$, delimiter$)
  3374.         If finish = 0 Then
  3375.             finish = Len(in$) + 1
  3376.         End If
  3377.  
  3378.         ReDim _Preserve result$(0 To UBound(result$) + 1)
  3379.  
  3380.         result$(UBound(result$)) = Mid$(in$, start, finish - start)
  3381.         start = finish + 1
  3382.     Loop While start <= Len(in$)
  3383. End Sub ' split
  3384.  
  3385. ' /////////////////////////////////////////////////////////////////////////////
  3386.  
  3387. Sub SplitTest
  3388.     Dim in$
  3389.     Dim delim$
  3390.     ReDim arrTest$(0)
  3391.     Dim iLoop%
  3392.  
  3393.     delim$ = Chr$(10)
  3394.     in$ = "this" + delim$ + "is" + delim$ + "a" + delim$ + "test"
  3395.     Print "in$ = " + Chr$(34) + in$ + Chr$(34)
  3396.     Print "delim$ = " + Chr$(34) + delim$ + Chr$(34)
  3397.     split in$, delim$, arrTest$()
  3398.  
  3399.     For iLoop% = LBound(arrTest$) To UBound(arrTest$)
  3400.         Print "arrTest$(" + LTrim$(RTrim$(Str$(iLoop%))) + ") = " + Chr$(34) + arrTest$(iLoop%) + Chr$(34)
  3401.     Next iLoop%
  3402.     Print
  3403.     Print "Split test finished."
  3404. End Sub ' SplitTest
  3405.  
  3406. ' /////////////////////////////////////////////////////////////////////////////
  3407.  
  3408. Sub SplitAndReplaceTest
  3409.     Dim in$
  3410.     Dim out$
  3411.     Dim iLoop%
  3412.     ReDim arrTest$(0)
  3413.  
  3414.     Print "-------------------------------------------------------------------------------"
  3415.     Print "SplitAndReplaceTest"
  3416.     Print
  3417.  
  3418.     Print "Original value"
  3419.     in$ = "This line 1 " + Chr$(13) + Chr$(10) + "and line 2" + Chr$(10) + "and line 3 " + Chr$(13) + "finally THE END."
  3420.     out$ = in$
  3421.     out$ = Replace$(out$, Chr$(13), "\r")
  3422.     out$ = Replace$(out$, Chr$(10), "\n")
  3423.     out$ = Replace$(out$, Chr$(9), "\t")
  3424.     Print "in$ = " + Chr$(34) + out$ + Chr$(34)
  3425.     Print
  3426.  
  3427.     Print "Fixing linebreaks..."
  3428.     in$ = Replace$(in$, Chr$(13) + Chr$(10), Chr$(13))
  3429.     in$ = Replace$(in$, Chr$(10), Chr$(13))
  3430.     out$ = in$
  3431.     out$ = Replace$(out$, Chr$(13), "\r")
  3432.     out$ = Replace$(out$, Chr$(10), "\n")
  3433.     out$ = Replace$(out$, Chr$(9), "\t")
  3434.     Print "in$ = " + Chr$(34) + out$ + Chr$(34)
  3435.     Print
  3436.  
  3437.     Print "Splitting up..."
  3438.     split in$, Chr$(13), arrTest$()
  3439.  
  3440.     For iLoop% = LBound(arrTest$) To UBound(arrTest$)
  3441.         out$ = arrTest$(iLoop%)
  3442.         out$ = Replace$(out$, Chr$(13), "\r")
  3443.         out$ = Replace$(out$, Chr$(10), "\n")
  3444.         out$ = Replace$(out$, Chr$(9), "\t")
  3445.         Print "arrTest$(" + cstr$(iLoop%) + ") = " + Chr$(34) + out$ + Chr$(34)
  3446.     Next iLoop%
  3447.     Print
  3448.  
  3449.     Print "SplitAndReplaceTest finished."
  3450. End Sub ' SplitAndReplaceTest
  3451.  
  3452. ' /////////////////////////////////////////////////////////////////////////////
  3453.  
  3454. Function StrPadRight$(sValue As String, iWidth As Integer)
  3455.     StrPadRight$ = left$( sValue + STRING$(iWidth, " "), iWidth)
  3456. End Function ' StrPadRight$
  3457.  
  3458. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3459. ' END GENERAL PURPOSE ROUTINES
  3460. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3461.  
  3462. ' #END
  3463. ' ################################################################################################################################################################
  3464.  

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: WIP: game input mapping for gamepad, keyboard, v0.90
« Reply #1 on: January 06, 2022, 10:12:58 pm »
  • The mapping part lets you map keys from the keyboard but for some reason the test routine that lets you move objects around the screen isn't reading the keyboard input.
  • Digital joysticks don't allow continuous movement.

If you don't get these figured out soon, perhaps take a look at my Zelda clone work. Mainly the Get_Input%% FUNCTION and Custom_Controls SUB. They might be of some help figuring out why you currently having trouble. Its got a few comments but feel free to ask if you still have trouble.

it would also be interesting to see how my setup works with a joypad that gives analog axis Values.

I recommend customizing the controls before you try actually playing the game, default START BUTTON is A(upper case) SELECT BUTTON is B(upper case)
the Options menu sliders are mouse controlled. And the custom controls menu is used with mouse too.

This is the last update of this version of my Zelda clone, as I messed the data structure up and cant get certain effects and extras to work. Also at some point I broke the ability to shoot the sword,(you still can but it throws some errors you have to OK though).
But you can watch the title sequence, explore the world, enter some caves, and collect the wood sword

Code: QB64: [Select]
  1. 'Zelda Clone take 2
  2.  
  3. TYPE ControllerKeys
  4.  KBCon_Up AS LONG
  5.  KBCon_Down AS LONG
  6.  KBCon_Left AS LONG
  7.  KBCon_Right AS LONG
  8.  KBCon_Select AS LONG
  9.  KBCon_Start AS LONG
  10.  KBCon_A_Button AS LONG
  11.  KBCon_B_Button AS LONG
  12.  Control_Pad AS _BYTE 'which Gamepad\Joystick Device Is player Using?
  13.  BAD_Pad AS _BYTE 'option for Buttons As Directions
  14.  Joy_Up AS _BYTE 'axis for up
  15.  Joy_Up_Val AS _BYTE 'axis value for up
  16.  Joy_Down AS _BYTE 'axis for down
  17.  Joy_Down_Val AS _BYTE 'axis value for down
  18.  Joy_Left AS _BYTE 'axis for left
  19.  Joy_Left_Val AS _BYTE 'axis value for left
  20.  Joy_Right AS _BYTE 'axis for right
  21.  Joy_Right_Val AS _BYTE 'axis value for right
  22.  Joy_Select AS _BYTE
  23.  Joy_Start AS _BYTE
  24.  Joy_A_Button AS _BYTE
  25.  Joy_B_Button AS _BYTE
  26.  Joy_Button_Up AS _BYTE 'for people whos controllers give BUTTON states for direction keys
  27.  Joy_Button_Down AS _BYTE 'or just want to use buttons for the direction keys.
  28.  Joy_Button_Left AS _BYTE '""""
  29.  Joy_Button_Right AS _BYTE 'however this fails to work so may not be implemented.
  30.  
  31. TYPE Game_Data
  32.  Impactflag AS _BYTE
  33.  Scale_Factor AS _BYTE
  34.  Scale_X AS _BYTE
  35.  Scale_Y AS _BYTE
  36.  NextScreen AS _BYTE
  37.  Wtime AS _BYTE
  38.  Atime AS _BYTE
  39.  Wframe AS _BYTE
  40.  Aframe AS _BYTE
  41.  Projectile_Count AS _BYTE 'Number of projectiles (including bombs) on screen
  42.  LoadedGame AS _BYTE '  which game is loaded?(1-3)
  43.  MSTR_Vol AS _BYTE '    Master Volume level
  44.  SFX_Vol AS _BYTE '     Sound FX Volume level
  45.  BGM_vol AS _BYTE '     Music Volume level
  46.  ControlType AS _BYTE ' player using Keyboard or Joypad?
  47.  Device_Count AS _BYTE 'Total input Devices aviliable
  48.  ItemCount AS _BYTE '   Number of items on screen
  49.  Collected AS _BYTE '   item link just picked up
  50.  
  51. TYPE Projectile_Data
  52.  Id AS _BYTE '       Kind of projectile; Sword\Arrow\Rock\Ball
  53.  Xloc AS INTEGER '   Projectile location
  54.  Yloc AS INTEGER '
  55.  Direction AS _BYTE 'Direction Projectile is traveling
  56.  Hits AS _BYTE '     Hits per projectile\(AKA:damage) in half heart increments
  57.  Owner AS _BYTE '    Who shot the projectile
  58.  
  59. TYPE Links_Data
  60.  '-----Position data-----
  61.  World AS _BYTE '           Player in Overworld or Underworld
  62.  World_X AS _BYTE '         Overworld X position
  63.  World_Y AS _BYTE '         Overworld Y position
  64.  Tile_X AS _UNSIGNED _BYTE 'Array X location:for Collision\entrances
  65.  Tile_Y AS _BYTE '          Array Y location:for Collision\entrances
  66.  Screen_X AS INTEGER '      X Position on screen
  67.  Screen_Y AS INTEGER '      Y Position on screen
  68.  Direction AS _BYTE '       Direction player is moving\facing
  69.  '-----------------------
  70.  '------Status Data------
  71.  Hearts AS _BYTE '    Units of health: 2 units(halves) per heart
  72.  Containers AS _BYTE 'Max number of health: total full hearts
  73.  Hits AS _BYTE '      Used when Link has defence rings;Blue = 2 hits per 1\2 heart, Red = 4 hits per 1\2 heart
  74.  Max_Bombs AS _BYTE ' how many bombs can Link carry
  75.  '-----------------------
  76.  '------Items Data-------
  77.  Sword AS _BYTE '      Which Sword does player have? Wooden-1,Silver-2,Magical-3
  78.  Weapon AS _BYTE '     Which Weapon is in hand A? Wooden-1,Silver-2,Magical-3,Wand-4
  79.  Ring AS _BYTE '       Which does player have? 0-none, 1-blue,2-red
  80.  Has_Bow AS _BYTE
  81.  Has_Wand AS _BYTE
  82.  Has_Bait AS _BYTE
  83.  Has_Arrow AS _BYTE
  84.  Has_Boomerang AS _BYTE
  85.  Has_Bombs AS _BYTE
  86.  Has_Bracelet AS _BYTE
  87.  Has_Book AS _BYTE
  88.  Has_MagicKey AS _BYTE
  89.  Has_Candle AS _BYTE ' 1-Blue, 2-Red
  90.  Has_Letter AS _BYTE ' 1-letter, 2-Blue Potion, 3-Red Potion
  91.  Has_Ladder AS _BYTE
  92.  Has_Raft AS _BYTE
  93.  Has_Ring AS _BYTE '   1-Blue, 2-Red
  94.  Has_Shield AS _BYTE ' Player has Magic Shield
  95.  Keys AS _BYTE '
  96.  Ruby AS _UNSIGNED _BYTE '
  97.  TriForce AS _BYTE '   each bit represents a collected piece
  98.  Has_Map AS INTEGER '  each bit represents a dungeon map
  99.  Has_Compass AS INTEGER ' each bit represents a dungeon compass
  100.  '-----------------------
  101.  '------Extra Data-------
  102.  Action AS _BYTE '          is player; using an item, Aquiring an item\sword, or aquiring a piece of the triforce?
  103.  Shot AS _BYTE '            player has shot sword
  104.  Projectile_id AS _BYTE '
  105.  Played AS _UNSIGNED _BYTE 'how many times has player played?
  106.  Beaten AS _BYTE '          Has player beaten Game 1? Game 2?
  107.  Shown_Letter AS _BYTE '    Has player shown Old woman the letter?
  108.  '-----------------------
  109.  
  110. TYPE Map_Data
  111.  Id AS _UNSIGNED _BYTE '      Tile id
  112.  Id2 AS _UNSIGNED _BYTE '     if hidden what is under top tile
  113.  Hidden AS _BYTE '            is there something under the tile?
  114.  Burnable AS _BYTE '          can the tile be burnt with candle?
  115.  Pushable AS _BYTE '          can the tile be moved by pushing?
  116.  Bombable AS _BYTE '          can the tile be blown up with bomb?
  117.  PushableXtra AS _BYTE '      can the tile be moved by pushing with braclet?
  118.  Is_Shop AS _BYTE '           is there a shop here?(shop\gift\gamble)
  119.  Is_Secret AS _BYTE '         secrets under bushes dont walk in, just jump in.
  120.  Is_Dungeon AS _BYTE '        is this a dungeon entrace? contains which one
  121.  Walkable AS _BYTE '          can the tile be walk upon?
  122.  Can_Awaken AS _BYTE '        touching the tile awakens the monster (graves and statues)
  123.  Is_Open AS _BYTE '           the player has blown up\burnt\pushed the tile out of the way
  124.  Linked_X AS _UNSIGNED _BYTE 'acts upon a different tile
  125.  Linked_Y AS _BYTE
  126.  Candle_Used AS _BYTE '       the player used the candle
  127.  Movement_Special AS _BYTE '  can the player move freely?
  128.  
  129.  Mob_List AS _UNSIGNED _BYTE 'Which Monsters appear on map segment
  130.  
  131. TYPE KeyCodeData
  132.  Nam AS STRING * 8
  133.  Value AS LONG
  134.  
  135. TYPE DEVICE_Info
  136.  Buttons AS INTEGER
  137.  Axis_p AS _BYTE
  138.  
  139. TYPE Drop_Data
  140.  ID AS _BYTE '         What is it?
  141.  Xloc AS INTEGER '     X location of the item\collision box
  142.  Yloc AS INTEGER '     Y location of the item\collision box
  143.  Xsiz AS _BYTE '       Xsize of the collision box
  144.  Ysiz AS _BYTE '       Ysize of the collision box
  145.  Is_Special AS _BYTE ' Is this a special pickup? pauses game while Link lifts the item
  146.  Death AS INTEGER '     items vanish after 90sec(by game count not timer) except for Special items
  147.  
  148. TYPE Item_Data
  149.  Sprite_X AS INTEGER 'Items sprite sheet location
  150.  Sprite_Y AS INTEGER '
  151.  BoxSize_X AS _BYTE 'Sprites size(8x8,8x16,16x16)
  152.  BoxSize_Y AS _BYTE
  153.  
  154. TYPE Shop_Data
  155.  Item AS _BYTE
  156.  
  157. CONST TRUE = -1, FALSE = NOT TRUE, None = 0
  158. CONST Up = 3, Right = 2, Left = 1, Down = 0, SELECT_BUTTON = 4, START_BUTTON = 5, BUTTON_B = 6, BUTTON_A = 7
  159. CONST OverWorld = 0, UnderWorld = 1
  160. CONST Walking = 1, Useing = 2, GetItem = 3, GetTriforce = 4, Attack = 5
  161. CONST Key_Right = 19712, Key_Left = 19200, Key_Up = 18432, Key_Down = 20480
  162. CONST Key_Space = 32, Key_Enter = 13
  163. CONST Default_Key_Right = 19712, Default_Key_Left = 19200, Default_Key_Up = 18432, Default_Key_Down = 20480
  164. CONST Default_A_Button = 32, Default_B_Button = 13, Default_Start_Button = 65, Default_Select_Button = 66
  165. CONST Sword = 1, Arrow = 2, Rock = 3, Ball = 4, Boomerang = 5
  166. CONST Player = 1, Monster = 2, KeyBoard = -1, JoyPad = 0, GO_IN = 1, GO_OUT = 2
  167. CONST Items = 0, Slash = 1, Stairs = 2, SwordShot = 3
  168.  
  169. DIM SHARED Layer(24) AS LONG, Hyrule(255, 87) AS Map_Data, Link AS Links_Data, Reset_Link AS Links_Data
  170. DIM SHARED C AS ControllerKeys, G AS Game_Data, P(16) AS Projectile_Data, Letter(44) AS STRING * 1
  171. DIM SHARED Offset_X(3) AS INTEGER, Offset_Y(3) AS INTEGER, Cave(15, 10) AS Map_Data
  172. DIM SHARED BGM(8) AS LONG, SFX(10) AS LONG, FFX(1) AS LONG, TFX(8) AS LONG
  173. DIM SHARED Records(3) AS Links_Data, Nick(3) AS STRING * 8 'loading\registering\elimination
  174. DIM SHARED KeyCodes(134) AS KeyCodeData, DeviceData(16) AS DEVICE_Info
  175. DIM SHARED Drop(17) AS Drop_Data, Item(37) AS Item_Data, Shop(17, 3) AS Shop_Data
  176. '-------------Graphical setup------------
  177. SCREEN _NEWIMAGE(800, 600, 32)
  178. Layer(0) = _DISPLAY
  179. Layer(1) = _NEWIMAGE(800, 600, 32) 'temp layer
  180. Layer(2) = _NEWIMAGE(640, 480, 256) 'palettized sprite sheet for color shifting
  181. Layer(3) = _NEWIMAGE(800, 600, 32) 'Map background prebuild layer
  182. Layer(4) = _NEWIMAGE(800, 600, 32) 'Mob layer
  183. Layer(5) = _NEWIMAGE(800, 600, 32) 'Sprite layer, moveable\burnable items + bomb holes + pickups
  184. Layer(6) = _NEWIMAGE(12288, 4224, 32) 'PreBuilt Map, upto 300%, for easier map scrolling.
  185. Layer(8) = _NEWIMAGE(800, 600, 32) 'debug map display
  186. Layer(16) = _NEWIMAGE(800, 600, 32) 'temp
  187. Layer(19) = _NEWIMAGE(800, 600, 32) 'item\creature collision layer
  188. MFI_Loader "Zelda.MFI"
  189. _FONT FFX(0), Layer(1)
  190. _FONT FFX(0), Layer(3)
  191. _FONT FFX(0), Layer(16)
  192. _CLEARCOLOR _RGB32(31), Layer(7)
  193. _CLEARCOLOR _RGB32(116), Layer(12)
  194. _CLEARCOLOR _RGB32(116), Layer(17)
  195. '----------------------------------------
  196. TFX(0) = _FREETIMER
  197. ON TIMER(TFX(0), .016) Flame_Burn_Cave
  198. '==================================
  199. G.Scale_Factor = 2
  200. G.Scale_X = 16 * G.Scale_Factor - 1
  201. G.Scale_Y = 16 * G.Scale_Factor - 1
  202. Link.Screen_X = Offset_X(G.Scale_Factor) + (16 * G.Scale_Factor * 7) + 8 * G.Scale_Factor '392
  203. Link.Screen_Y = Offset_Y(G.Scale_Factor) + (16 * G.Scale_Factor * 5) '292
  204. Nick(0) = "": Nick(1) = "": Nick(2) = "": Nick(3) = ""
  205. G.BGM_vol = 66
  206. G.SFX_Vol = 75
  207. G.MSTR_Vol = 100
  208. 'OPEN "debug.txt" FOR OUTPUT AS #6
  209. '==================================
  210.  
  211. Get_JoyPads
  212. G.ControlType = KeyBoard
  213. Title_Screen
  214. Select_Screen
  215.  
  216. Build_Map_Screen 16 * Link.World_X, 11 * Link.World_Y
  217. ClearLayer Layer(6)
  218. Build_Map_in_Totallity 'prebuild the entire map at the current scale factor
  219. ClearLayer Layer(1)
  220. _SOURCE Layer(19)
  221.  IF Link.Action <> GetItem THEN
  222.   SELECT CASE Get_Input
  223.    CASE BUTTON_A
  224.     IF Link.Sword <> FALSE THEN
  225.      IF G.Aframe = 0 AND G.Atime = 0 THEN _SNDPLAY SFX(Slash): Link.Action = Attack: Press = Press + 1
  226.      Check_Link_Sword_Shot
  227.     END IF
  228.    CASE Up
  229.     IF Link.Action <> Attack THEN Link.Direction = Up: Link.Action = Walking
  230.    CASE Down
  231.     IF Link.Action <> Attack THEN Link.Direction = Down: Link.Action = Walking
  232.    CASE Right
  233.     IF Link.Action <> Attack THEN Link.Direction = Right: Link.Action = Walking
  234.    CASE Left
  235.     IF Link.Action <> Attack THEN Link.Direction = Left: Link.Action = Walking
  236.    CASE ELSE
  237.     IF Link.Action = Walking THEN Link.Action = None
  238.  
  239.  IF Link.Action = GetItem OR Link.Action = GetTriforce THEN
  240.   IF NOT _SNDPLAYING(SFX(Items)) THEN Link.Action = None: G.Collected = -1
  241.  IF Link.World = OverWorld AND Hyrule(Link.Tile_X, Link.Tile_Y).Is_Shop THEN Enter_Shop Hyrule(Link.Tile_X, Link.Tile_Y).Is_Shop
  242.  '-------Items code----------
  243.  IF G.ItemCount THEN Age_items 'only run if there are items to age
  244.  IF Link_Object_Collision THEN Pickup_Item G.Collected
  245.  '---------------------------
  246.  '------Graphx build------
  247.  _PUTIMAGE , Layer(3), Layer(1)
  248.  _PUTIMAGE , Layer(5), Layer(1)
  249.  IF Link.Action = Walking THEN Move_Link
  250.  IF Link.Shot THEN Move_Sword_Shot
  251.  IF G.Impactflag THEN Impact 0, 0
  252.  IF G.ItemCount THEN Display_drops
  253.  IF Link.Action = GetItem THEN Display_Lifted_Item
  254.  _PRINTSTRING (0, 570), STR$(Link.Action) + STR$(G.Collected), Layer(1)
  255.  Place_Link
  256.  IF G.Scale_Factor = 1 THEN _PUTIMAGE , Layer(8), Layer(1)
  257.  IF G.Scale_Factor = 1 THEN _DEST Layer(1): LINE (100 + 2 * Link.Tile_X, 0 + 2 * Link.Tile_Y)-STEP(1, 1), _RGB32(255, 255, 0), BF
  258.  _PUTIMAGE , Layer(1), Layer(0)
  259.  ClearLayer Layer(1)
  260.  '------------------------
  261.  IF INKEY$ = CHR$(27) THEN ExitFlag%% = TRUE
  262.  _LIMIT 60
  263. LOOP UNTIL ExitFlag%%
  264.  
  265. _PUTIMAGE , Layer(19), Layer(0)
  266. SUB Add_Projectile (What%%, Who%%)
  267.  SELECT CASE What%%
  268.   CASE Sword
  269.    P(G.Projectile_Count).Owner = Who%%
  270.    P(G.Projectile_Count).Id = Sword
  271.    P(G.Projectile_Count).Direction = Link.Direction
  272.    IF Who%% = Player THEN
  273.     P(G.Projectile_Count).Xloc = Link.Screen_X
  274.     P(G.Projectile_Count).Yloc = Link.Screen_Y
  275.    ELSE
  276.    END IF
  277.    G.Projectile_Count = G.Projectile_Count + 1
  278.  
  279. SUB Age_items
  280.  FOR i%% = 0 TO G.ItemCount - 1
  281.   IF Drop(i%%).Death > 0 THEN Drop(i%%).Death = Drop(i%%).Death - 1 'only age normal drops and live drops
  282.   IF Drop(i%%).Death = 0 THEN
  283.    Reduce%% = Reduce%% + 1
  284.    Kill_Item i%%
  285.   END IF
  286.  NEXT i%%
  287.  G.ItemCount = G.ItemCount - Reduce%%
  288.  IF Reduce%% <> 0 THEN Build_Item_Collision_layer 'update the collsion layer if some items have died off
  289.  
  290. SUB Remove_Projectile (What%%, Who%%)
  291.  SELECT CASE What%%
  292.   CASE Sword
  293.    IF Who%% = Player THEN
  294.     G.Projectile_Count = G.Projectile_Count - 1
  295.    END IF
  296.  
  297. SUB Build_Cave_Screen
  298.  FOR Y%% = 0 TO 10
  299.   FOR X%% = 0 TO 15
  300.    Place_Tile_On_Screen (16 * G.Scale_Factor) * X%%, (16 * G.Scale_Factor) * Y%%, Cave(X%%, Y%%).Id, Layer(3)
  301.   NEXT
  302.  
  303. SUB Build_Item_Collision_layer
  304.  _DEST Layer(19)
  305.  LINE (0, 0)-STEP(799, 599), _RGB32(0), BF 'faster than CLS???
  306.  FOR i%% = 0 TO G.ItemCount - 1
  307.   LINE (Drop(i%%).Xloc, Drop(i%%).Yloc)-STEP(Item(Drop(i%%).ID).BoxSize_X * G.Scale_Factor - 1, Item(Drop(i%%).ID).BoxSize_Y * G.Scale_Factor - 1), _RGB32(1 + i%%, 0, 100), BF 'item mask for collision
  308.  NEXT i%%
  309.  _DEST Layer(1)
  310.  
  311. SUB Build_Map_Screen (Map_X~%%, Map_Y%%)
  312.  _DEST Layer(8)
  313.  FOR Y%% = 0 TO 10
  314.   FOR X%% = 0 TO 15
  315.    Place_Tile_On_Screen (16 * G.Scale_Factor) * X%%, (16 * G.Scale_Factor) * Y%%, Hyrule(Map_X~%% + X%%, Map_Y%% + Y%%).Id, Layer(3)
  316.   NEXT
  317.  FOR Y%% = 0 TO 87
  318.   FOR x~%% = 0 TO 255
  319.    IF Hyrule(x~%%, Y%%).Walkable = FALSE THEN LINE (100 + 2 * x~%%, 0 + 2 * Y%%)-STEP(1, 1), _RGB32(255), BF
  320.   NEXT
  321.  _DEST Layer(0)
  322.  
  323. SUB Build_Map_in_Totallity
  324.  FOR Y%% = 0 TO 87
  325.   FOR X~%% = 0 TO 255
  326.    tile~%% = Hyrule(X~%%, Y%%).Id
  327.    Gy% = 17 * (tile~%% \ 20) 'get which row it comes from
  328.    Gx% = 17 * (tile~%% MOD 20) 'which column position
  329.    _PUTIMAGE ((16 * G.Scale_Factor) * X~%%, (16 * G.Scale_Factor) * Y%%)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(6), (1 + Gx%, 1 + Gy%)-STEP(15, 15)
  330.   NEXT
  331.  
  332. SUB Check_Link_Location
  333.  Ax% = Link.World_X * 16 * (16 * G.Scale_Factor) 'gets the Left most pixel point for the current map screen
  334.  Ay% = Link.World_Y * 11 * (16 * G.Scale_Factor) 'gets the Top most pixel point for the current map screen
  335.  Tx~%% = (Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  336.  Ty%% = (Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) - 2 + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  337.  IF Hyrule(Tx~%%, Ty%%).Is_Shop THEN Enter_Shop Hyrule(Tx~%%, Ty%%).Is_Shop
  338.  
  339.  
  340.  
  341. SUB Check_Link_Sword_Shot 'Can Link shoot his sword?
  342.  IF Link.Hearts = Link.Containers * 2 THEN 'Link has full hearts and can shoot
  343.   IF NOT _SNDPLAYING(SFX(SwordShot)) THEN 'Link did not just shoot
  344.    IF NOT G.Impactflag THEN 'Link's last shot has finished
  345.     IF NOT Link.Shot THEN
  346.      _SNDPLAY SFX(SwordShot)
  347.      Link.Shot = TRUE
  348.      Add_Projectile Sword, Player
  349.     END IF
  350.    END IF
  351.   END IF
  352.  
  353. SUB ClearLayer (L&)
  354.  _DEST L&
  355.  CLS
  356.  
  357. SUB ClearLayerTrans (L&)
  358.  _DEST L&
  359.  CLS , 0
  360.  
  361. SUB DarkenImage (Image AS LONG, Value_From_0_To_1 AS SINGLE)
  362.  IF Value_From_0_To_1 <= 0 OR Value_From_0_To_1 >= 1 OR _PIXELSIZE(Image) <> 4 THEN EXIT SUB
  363.  DIM Buffer AS _MEM: Buffer = _MEMIMAGE(Image) 'Get a memory reference to our image
  364.  DIM Frac_Value AS LONG: Frac_Value = Value_From_0_To_1 * 65536 'Used to avoid slow floating point calculations
  365.  DIM O AS _OFFSET, O_Last AS _OFFSET
  366.  O = Buffer.OFFSET 'We start at this offset
  367.  O_Last = Buffer.OFFSET + _WIDTH(Image) * _HEIGHT(Image) * 4 'We stop when we get to this offset
  368.  'use on error free code ONLY!
  369.  DO
  370.   _MEMPUT Buffer, O, _MEMGET(Buffer, O, _UNSIGNED _BYTE) * Frac_Value \ 65536 AS _UNSIGNED _BYTE
  371.   _MEMPUT Buffer, O + 1, _MEMGET(Buffer, O + 1, _UNSIGNED _BYTE) * Frac_Value \ 65536 AS _UNSIGNED _BYTE
  372.   _MEMPUT Buffer, O + 2, _MEMGET(Buffer, O + 2, _UNSIGNED _BYTE) * Frac_Value \ 65536 AS _UNSIGNED _BYTE
  373.   O = O + 4
  374.  LOOP UNTIL O = O_Last
  375.  'turn checking back on when done!
  376.  _MEMFREE Buffer
  377.  
  378. SUB Display_drops
  379.  FOR i%% = 0 TO G.ItemCount
  380.   _PUTIMAGE (Drop(i%%).Xloc, Drop(i%%).Yloc)-STEP(Item(Drop(i%%).ID).BoxSize_X * G.Scale_Factor - 1, Item(Drop(i%%).ID).BoxSize_Y * G.Scale_Factor - 1), Layer(7), Layer(1), (Item(Drop(i%%).ID).Sprite_X, Item(Drop(i%%).ID).Sprite_Y)-STEP(Item(Drop(i%%).ID).BoxSize_X - 1, Item(Drop(i%%).ID).BoxSize_Y - 1)
  381.  NEXT i%%
  382.  
  383. SUB Display_Lifted_Item
  384.  _PUTIMAGE (Link.Screen_X, Link.Screen_Y - 16 * G.Scale_Factor)-STEP(Item(G.Collected + 1).BoxSize_X * G.Scale_Factor - 1, Item(G.Collected + 1).BoxSize_Y * G.Scale_Factor - 1), Layer(7), Layer(1), (Item(G.Collected + 1).Sprite_X, Item(G.Collected + 1).Sprite_Y)-STEP(Item(G.Collected + 1).BoxSize_X - 1, Item(G.Collected + 1).BoxSize_Y - 1)
  385.  
  386. SUB Drop_Item (Id%%, X%, Y%, Status%%)
  387.  Drop(G.ItemCount).ID = Id%%
  388.  Drop(G.ItemCount).Xloc = X%
  389.  Drop(G.ItemCount).Yloc = Y%
  390.  Drop(G.ItemCount).Xsiz = Item(Id%%).BoxSize_X * G.Scale_Factor
  391.  Drop(G.ItemCount).Ysiz = Item(Id%%).BoxSize_X * G.Scale_Factor
  392.  Drop(G.ItemCount).Is_Special = Status%%
  393.  IF Status%% = FALSE THEN
  394.   Drop(G.ItemCount).Death = 5400 'clicks[60 per second]
  395.   Drop(G.ItemCount).Death = -1 'special items dont die
  396.  G.ItemCount = G.ItemCount + 1
  397.  Build_Item_Collision_layer
  398.  
  399. SUB Enter_Shop (Which%%)
  400.  'covers all cave screens; Shop\Gamble\Old Man\Old Woman\Information\Gifts\ and Thefts
  401.  IF NOT Hyrule(Link.Tile_X, Link.Tile_Y).Burnable THEN Walk_InOut GO_IN
  402.  Build_Cave_Screen
  403.  X% = Offset_X(G.Scale_Factor)
  404.  Y% = Offset_Y(G.Scale_Factor)
  405.  _PUTIMAGE , Layer(3), Layer(1)
  406.  Link.World = UnderWorld
  407.  SELECT CASE Which%%
  408.   CASE 1 'get the wood sword
  409.    'place the old man
  410.    IF Link.Sword = 0 THEN 'only show if player has not taken the wooden sword yet.
  411.     _PRINTSTRING (X% + 16 * G.Scale_Factor + 64, Y% + 16 * G.Scale_Factor + 48), "It's dangerous to go", Layer(3)
  412.     _PRINTSTRING (X% + 16 * G.Scale_Factor + 96, Y% + 16 * G.Scale_Factor + 64), "alone, take this.", Layer(3)
  413.     _PUTIMAGE (X% + (16 * G.Scale_Factor * 7.5), Y% + (16 * G.Scale_Factor * 4))-STEP(G.Scale_X, G.Scale_Y), Layer(17), Layer(3), (1, 11)-STEP(15, 15)
  414.     Drop_Item Shop(1, 1).Item, X% + (16 * G.Scale_Factor * 7.75), Y% + (16 * G.Scale_Factor * 5.5), TRUE
  415.    END IF
  416.   CASE 2 'Shop 1: Sheild 160, Key 100, Blue Candle 60
  417.   CASE 3 'old woman with life potions
  418.   CASE 4 'Shop 2: Sheild 130, Bomb 20, Arrow 80
  419.   CASE 5 'Shop 3: Blue Ring 250, Key 80, Bait 60
  420.   CASE 6 'Old woman:"Meet the Old man":"At the Grave"
  421.   CASE 7 'Old Woman: Informant
  422.   CASE 8 'Old Man: Life bottle or Heart Container "Take any one you want"
  423.   CASE 9 'Moblin: Gambling game
  424.   CASE 10 'Old Man: "Show this to the":"Old woman" Get the Letter
  425.  
  426. SUB Fade_Out (L&)
  427.  FOR n! = 1 TO 0.5 STEP -0.05
  428.   i2& = _COPYIMAGE(L&)
  429.   DarkenImage i2&, n!
  430.   _PUTIMAGE (0, 0), i2&, Layer(0)
  431.   _FREEIMAGE i2&
  432.   _DELAY .03
  433.  
  434. SUB Fade_In (L&)
  435.  FOR n! = 0.01 TO 1 STEP 0.05
  436.   i2& = _COPYIMAGE(L&)
  437.   DarkenImage i2&, n!
  438.   _PUTIMAGE (0, 0), i2&, Layer(0)
  439.   _FREEIMAGE i2&
  440.   _DELAY .03
  441.  
  442. FUNCTION Find_First_Available%% (Which%%, Start%%)
  443.  Selection%% = Start%% 'always start at the first saved game slot then check
  444.  DO 'lets find the first available selection (if there are any saved games)
  445.   IF Selection%% < 4 THEN
  446.    IF Which%% = 0 THEN IF RTRIM$(Nick(Selection%%)) = "" THEN Selection%% = Selection%% + 1 ELSE Good_Selection%% = TRUE
  447.    IF Which%% = 1 THEN IF RTRIM$(Nick(Selection%%)) = "" THEN Good_Selection%% = TRUE ELSE Selection%% = Selection%% + 1
  448.   ELSE '4 and 5 are always good selections
  449.    Good_Selection%% = TRUE
  450.   END IF
  451.  LOOP UNTIL Good_Selection%%
  452.  Find_First_Available = Selection%%
  453.  
  454. SUB Flame_Burn_Cave
  455.  STATIC Frame%%, Tick%%
  456.  X% = Offset_X(G.Scale_Factor)
  457.  Y% = Offset_Y(G.Scale_Factor)
  458.  _PUTIMAGE (X% + (16 * G.Scale_Factor * 4.5), Y% + (16 * G.Scale_Factor * 4))-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(3), (290, 256)-STEP(15, 15)
  459.  _PUTIMAGE (X% + (16 * G.Scale_Factor * 10.5), Y% + (16 * G.Scale_Factor * 4))-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(3), (290, 256)-STEP(15, 15)
  460.  IF Frame%% THEN
  461.   _PUTIMAGE (X% + (16 * G.Scale_Factor * 4.5), Y% + (16 * G.Scale_Factor * 4))-STEP(G.Scale_X, G.Scale_Y), Layer(17), Layer(3), (52, 11)-STEP(15, 15)
  462.   _PUTIMAGE (X% + (16 * G.Scale_Factor * 10.5), Y% + (16 * G.Scale_Factor * 4))-STEP(G.Scale_X, G.Scale_Y), Layer(17), Layer(3), (52, 11)-STEP(15, 15)
  463.   _PUTIMAGE (X% + (16 * G.Scale_Factor * 5.5), Y% + (16 * G.Scale_Factor * 4))-STEP(-G.Scale_X, G.Scale_Y), Layer(17), Layer(3), (52, 11)-STEP(15, 15)
  464.   _PUTIMAGE (X% + (16 * G.Scale_Factor * 11.5), Y% + (16 * G.Scale_Factor * 4))-STEP(-G.Scale_X, G.Scale_Y), Layer(17), Layer(3), (52, 11)-STEP(15, 15)
  465.  Tick%% = Tick%% + 1
  466.  IF Tick%% = 8 THEN Frame%% = NOT Frame%%: Tick%% = 0
  467.  
  468. FUNCTION Get_Input%% ()
  469.  Result%% = TRUE '-1 for no input
  470.  SELECT CASE G.ControlType
  471.   CASE TRUE 'Keyboard input
  472.    IF _KEYDOWN(C.KBCon_Up) THEN Result%% = Up
  473.    IF _KEYDOWN(C.KBCon_Down) THEN Result%% = Down
  474.    IF _KEYDOWN(C.KBCon_Left) THEN Result%% = Left
  475.    IF _KEYDOWN(C.KBCon_Right) THEN Result%% = Right
  476.    IF _KEYDOWN(C.KBCon_Select) THEN Result%% = SELECT_BUTTON: ' DO: LOOP WHILE _KEYDOWN(C.KBCon_Select)
  477.    IF _KEYDOWN(C.KBCon_Start) THEN Result%% = START_BUTTON: ' DO: LOOP WHILE _KEYDOWN(C.KBCon_Start)
  478.    IF _KEYDOWN(C.KBCon_A_Button) THEN Result%% = BUTTON_A: ' DO: LOOP WHILE _KEYDOWN(C.KBCon_A_Button)
  479.    IF _KEYDOWN(C.KBCon_B_Button) THEN Result%% = BUTTON_B: ' DO: LOOP WHILE _KEYDOWN(C.KBCon_B_Button)
  480.   CASE FALSE 'joystick input
  481.    IF C.Control_Pad THEN
  482.     DO: LOOP WHILE _DEVICEINPUT(C.Control_Pad)
  483.     IF NOT C.BAD_Pad THEN
  484.      nul%% = AxisPower(CJR%%, CJL%%, CJU%%, CJD%%) 'read directional axis values
  485.      IF CJU%% THEN Result%% = Up
  486.      IF CJD%% THEN Result%% = Down
  487.      IF CJL%% THEN Result%% = Left
  488.      IF CJR%% THEN Result%% = Right
  489.     ELSE
  490.      IF _BUTTON(C.Joy_Button_Up) THEN Result%% = Up ': Joy_Lock_Button (C.Joy_Button_Up)
  491.      IF _BUTTON(C.Joy_Button_Down) THEN Result%% = Down ': Joy_Lock_Button (C.Joy_Button_Down)
  492.      IF _BUTTON(C.Joy_Button_Left) THEN Result%% = Left ': Joy_Lock_Button (C.Joy_Button_Left)
  493.      IF _BUTTON(C.Joy_Button_Right) THEN Result%% = Right ': Joy_Lock_Button (C.Joy_Button_Right)
  494.     END IF
  495.     IF _BUTTON(C.Joy_Select) THEN Result%% = SELECT_BUTTON ': Joy_Lock_Button (C.Joy_Select)
  496.     IF _BUTTON(C.Joy_Start) THEN Result%% = START_BUTTON ': Joy_Lock_Button (C.Joy_Start)
  497.     IF _BUTTON(C.Joy_A_Button) THEN Result%% = BUTTON_A ': Joy_Lock_Button (C.Joy_A_Button)
  498.     IF _BUTTON(C.Joy_B_Button) THEN Result%% = BUTTON_B ': Joy_Lock_Button (C.Joy_B_Button)
  499.    END IF
  500.  Get_Input = Result%%
  501.  
  502. SUB Impact (X%, Y%)
  503.  STATIC Frame AS _BYTE, Fstp AS _BYTE, Xloc(3) AS INTEGER, Yloc(3) AS INTEGER
  504.  IF Frame = -1 OR Xloc(2) = 0 THEN
  505.   Fstp = 0
  506.   FOR i%% = 0 TO 3: Xloc(i%%) = X% + 8 * G.Scale_Factor: Yloc(i%%) = Y% + 8 * G.Scale_Factor: NEXT i%%
  507.  Xloc(0) = Xloc(0) - G.Scale_Factor: Yloc(0) = Yloc(0) - G.Scale_Factor
  508.  Xloc(1) = Xloc(1) + G.Scale_Factor: Yloc(1) = Yloc(1) - G.Scale_Factor
  509.  Xloc(2) = Xloc(2) + G.Scale_Factor: Yloc(2) = Yloc(2) + G.Scale_Factor
  510.  Xloc(3) = Xloc(3) - G.Scale_Factor: Yloc(3) = Yloc(3) + G.Scale_Factor
  511.  FOR i%% = 0 TO 3
  512.   SELECT CASE i%%
  513.    CASE 0
  514.     _PUTIMAGE (Xloc(i%%) - 16, Yloc(i%%) - 16)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (205 + 17 * Frame, 205)-STEP(15, 15)
  515.    CASE 1
  516.     _PUTIMAGE (Xloc(i%%) + 16, Yloc(i%%) - 16)-STEP(-G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (205 + 17 * Frame, 205)-STEP(15, 15)
  517.    CASE 2
  518.     _PUTIMAGE (Xloc(i%%) + 16, Yloc(i%%) + 16)-STEP(-G.Scale_X, -G.Scale_Y), Layer(7), Layer(1), (205 + 17 * Frame, 205)-STEP(15, 15)
  519.    CASE 3
  520.     _PUTIMAGE (Xloc(i%%) - 16, Yloc(i%%) + 16)-STEP(G.Scale_X, -G.Scale_Y), Layer(7), Layer(1), (205 + 17 * Frame, 205)-STEP(15, 15)
  521.  NEXT i%%
  522.  Fstp = Fstp + 1
  523.  Frame = Frame + 1
  524.  IF Frame = 4 THEN Frame = 0
  525.  IF Fstp = 16 THEN Frame = -1: G.Impactflag = FALSE
  526.  
  527. SUB Kill_Item (I%%)
  528.  Drop(I%%) = Drop(17) 'erase data in this drop(#17 is always blank)
  529.  FOR j%% = I%% TO G.ItemCount
  530.   SWAP Drop(j%%), Drop(I%%)
  531.  NEXT j%%
  532.  
  533. SUB Link_Attack
  534.  SELECT CASE Link.Direction
  535.   CASE Up
  536.    Ox%% = 0: Oy%% = -14 * G.Scale_Factor
  537.   CASE Down
  538.    Ox%% = 0: Oy%% = 14 * G.Scale_Factor
  539.   CASE Left
  540.    Ox%% = -14 * G.Scale_Factor + G.Aframe * (4 * G.Scale_Factor): Oy%% = 0
  541.   CASE Right
  542.    Ox%% = 14 * G.Scale_Factor - G.Aframe * (4 * G.Scale_Factor): Oy%% = 0
  543.  SELECT CASE G.Aframe
  544.   CASE 0
  545.    _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 17 * (Link.Direction + 8), 137)-STEP(15, 15)
  546.   CASE 1
  547.    _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 17 * (Link.Direction + 8), 137)-STEP(15, 15)
  548.    _PUTIMAGE (Link.Screen_X + Ox%%, Link.Screen_Y + Oy%%)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 51 * Link.Weapon, 171 + 17 * Link.Direction)-STEP(15, 15)
  549.   CASE 2
  550.    _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 34 * Link.Direction + 17, 137)-STEP(15, 15)
  551.    _PUTIMAGE (Link.Screen_X + Ox%%, Link.Screen_Y + Oy%%)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 51 * Link.Weapon + 17, 171 + 17 * Link.Direction)-STEP(15, 15)
  552.   CASE 3
  553.    _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 34 * Link.Direction + 0, 137)-STEP(15, 15)
  554.    _PUTIMAGE (Link.Screen_X + Ox%%, Link.Screen_Y + Oy%%)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 51 * Link.Weapon + 34, 171 + 17 * Link.Direction)-STEP(15, 15)
  555.   CASE ELSE 'attack animation finished
  556.    G.Atime = 0: G.Aframe = 0: Link.Action = None
  557.    _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 34 * Link.Direction + 17 * Frame%%, 137)-STEP(15, 15)
  558.  
  559. FUNCTION Link_Collision%% (Dir%%)
  560.  Result%% = FALSE 'start at no collision
  561.  SELECT CASE Link.World
  562.   CASE OverWorld
  563.    'get links center point x\y tile position to check for collision
  564.    Ax% = Link.World_X * 16 * (16 * G.Scale_Factor) 'gets the Left most pixel point for the current map screen
  565.    Ay% = Link.World_Y * 11 * (16 * G.Scale_Factor) 'gets the Top most pixel point for the current map screen
  566.    'we now have the tile that Links center pixel is in!
  567.    SELECT CASE Dir%%
  568.     CASE Up
  569.      Tx~%% = (Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  570.      Ty%% = (Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) - 2 + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  571.      IF NOT Hyrule(Tx~%%, Ty%%).Walkable THEN Result%% = TRUE
  572.     CASE Down
  573.      Tx~%% = (Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  574.      Ty%% = (2 + Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) + 2 * G.Scale_Factor + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  575.      IF NOT Hyrule(Tx~%%, Ty%%).Walkable THEN Result%% = TRUE
  576.     CASE Left
  577.      Tx~%% = (Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) - 2 + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  578.      Ty%% = (Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  579.      IF NOT Hyrule(Tx~%%, Ty%%).Walkable THEN Result%% = TRUE
  580.     CASE Right
  581.      Tx~%% = (2 + Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) + 2 * G.Scale_Factor + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  582.      Ty%% = (Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  583.      IF NOT Hyrule(Tx~%%, Ty%%).Walkable THEN Result%% = TRUE
  584.    END SELECT
  585.    Link.Tile_X = Tx~%%
  586.    Link.Tile_Y = Ty%%
  587.   CASE UnderWorld
  588.    'get links center point x\y tile position to check for collision
  589.    Ax% = 0 'Link.World_X * 16 * (16 * G.Scale_Factor) 'gets the Left most pixel point for the current map screen
  590.    Ay% = 0 'Link.World_Y * 11 * (16 * G.Scale_Factor) 'gets the Top most pixel point for the current map screen
  591.    'we now have the tile that Links center pixel is in!
  592.    SELECT CASE Dir%%
  593.     CASE Up
  594.      Tx~%% = (Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  595.      Ty%% = (Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) - 2 + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  596.      IF NOT Cave(Tx~%%, Ty%%).Walkable THEN Result%% = TRUE
  597.     CASE Down
  598.      Tx~%% = (Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  599.      Ty%% = (2 + Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) + 2 * G.Scale_Factor + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  600.      IF NOT Cave(Tx~%%, Ty%%).Walkable THEN Result%% = TRUE
  601.     CASE Left
  602.      Tx~%% = (Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) - 2 + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  603.      Ty%% = (Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  604.      IF NOT Cave(Tx~%%, Ty%%).Walkable THEN Result%% = TRUE
  605.     CASE Right
  606.      Tx~%% = (2 + Ax% + Link.Screen_X - Offset_X(G.Scale_Factor) + 2 * G.Scale_Factor + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent x-offset+50% sprite size
  607.      Ty%% = (Ay% + Link.Screen_Y - Offset_Y(G.Scale_Factor) + (16 * G.Scale_Factor \ 2)) \ 16 * G.Scale_Factor 'now add curent y-offset+50% sprite size
  608.      IF NOT Cave(Tx~%%, Ty%%).Walkable THEN Result%% = TRUE
  609.    END SELECT
  610.  Link_Collision = Result%%
  611.  
  612. FUNCTION Link_Object_Collision%%
  613.  Result%% = FALSE
  614.  FOR y%% = 0 TO 8 * G.Scale_Factor
  615.   FOR x%% = 0 TO 8 * G.Scale_Factor
  616.    IF _RED32(POINT(x%% + Link.Screen_X + 4 * G.Scale_Factor, y%% + Link.Screen_Y + 4 * G.Scale_Factor)) <> 0 THEN
  617.     G.Collected = _RED32(POINT(x%% + Link.Screen_X + 4 * G.Scale_Factor, y%% + Link.Screen_Y + 4 * G.Scale_Factor)) - 1
  618.     Result%% = TRUE
  619.     x%% = 8 * G.Scale_Factor + 1: y%% = 8 * G.Scale_Factor + 1
  620.    END IF
  621.   NEXT x%%
  622.  NEXT y%%
  623.  _PRINTSTRING (0, 0), STR$(G.Collected), Layer(3)
  624.  Link_Object_Collision = Result%%
  625.  
  626. SUB Move_Link
  627.  SELECT CASE Link.Direction
  628.   CASE Up
  629.    IF NOT Link_Collision(Up) THEN 'nothing blocking Link
  630.     IF Link.Screen_Y > Offset_Y(G.Scale_Factor) THEN 'Link is not at the edge of the screen
  631.      Link.Screen_Y = Link.Screen_Y - 2 * G.Scale_Factor
  632.     ELSE 'player is at edge of screen to shift to next one.
  633.      G.NextScreen = Up
  634.      Shift_New_Map_Screen
  635.     END IF
  636.    END IF
  637.   CASE Down
  638.    IF NOT Link_Collision(Down) THEN 'nothing blocking Link
  639.     IF Link.Screen_Y < (Offset_Y(G.Scale_Factor) + (16 * G.Scale_Factor * 10)) THEN 'Link is not at the edge of the screen
  640.      Link.Screen_Y = Link.Screen_Y + 2 * G.Scale_Factor
  641.     ELSEIF Link.World = OverWorld THEN 'player is at edge of screen to shift to next one.
  642.      G.NextScreen = Down
  643.      Shift_New_Map_Screen
  644.     ELSE 'link is in the underworld
  645.      Walk_InOut GO_OUT
  646.     END IF
  647.    END IF
  648.   CASE Left
  649.    IF NOT Link_Collision(Left) THEN 'nothing blocking Link
  650.     IF Link.Screen_X > Offset_X(G.Scale_Factor) THEN 'Link is not at the edge of the screen
  651.      Link.Screen_X = Link.Screen_X - 2 * G.Scale_Factor
  652.     ELSE 'player is at edge of screen to shift to next one.
  653.      G.NextScreen = Left
  654.      Shift_New_Map_Screen
  655.     END IF
  656.    END IF
  657.   CASE Right
  658.    IF NOT Link_Collision(Right) THEN 'nothing blocking Link
  659.     IF Link.Screen_X < (Offset_X(G.Scale_Factor) + (16 * G.Scale_Factor * 15)) THEN 'Link is not at the edge of the screen
  660.      Link.Screen_X = Link.Screen_X + 2 * G.Scale_Factor
  661.     ELSE 'player is at edge of screen to shift to next one.
  662.      G.NextScreen = Right
  663.      Shift_New_Map_Screen
  664.     END IF
  665.    END IF
  666.  
  667.  
  668. SUB Move_Sword_Shot
  669.  STATIC Xloc AS INTEGER, Yloc AS INTEGER, Direction AS _BYTE, Fstp AS _BYTE, Frame AS _BYTE
  670.  IF Direction = -1 OR Xloc = 0 THEN 'if no direction assigned then assign one
  671.   Direction = Link.Direction
  672.   Xloc = Link.Screen_X
  673.   Yloc = Link.Screen_Y
  674.  SELECT CASE Direction
  675.   CASE Up
  676.    Yloc = Yloc - 4 * G.Scale_Factor
  677.   CASE Down
  678.    Yloc = Yloc + 4 * G.Scale_Factor
  679.   CASE Left
  680.    Xloc = Xloc - 4 * G.Scale_Factor
  681.   CASE Right
  682.    Xloc = Xloc + 4 * G.Scale_Factor
  683.  _PUTIMAGE (Xloc, Yloc)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 17 * Frame + 68 * Direction, 239)-STEP(15, 15)
  684.  SELECT CASE G.Scale_Factor
  685.   CASE 1
  686.    IF Xloc <= 272 OR Xloc >= 514 OR Yloc <= 212 OR Yloc >= 372 THEN Done%% = TRUE
  687.   CASE 2
  688.    IF Xloc <= 140 OR Xloc >= 630 OR Yloc <= 100 OR Yloc >= 430 THEN Done%% = TRUE
  689.   CASE 3
  690.    IF Xloc <= 16 OR Xloc >= 744 OR Yloc <= 96 OR Yloc >= 559 THEN Done%% = TRUE
  691.  IF Done%% THEN Link.Shot = FALSE: G.Impactflag = TRUE: Impact Xloc, Yloc: Direction = -1: Remove_Projectile Sword, Player
  692.  Fstp = Fstp + 1
  693.  IF Fstp = 2 THEN Fstp = 0: Frame = Frame + 1
  694.  IF Frame = 4 THEN Frame = 0
  695.  
  696. SUB Pickup_Item (Id%%)
  697.  X% = Offset_X(G.Scale_Factor)
  698.  Y% = Offset_Y(G.Scale_Factor)
  699.  IF Drop(Id%%).Is_Special THEN _SNDPLAY SFX(Items): Link.Action = GetItem
  700.  SELECT CASE Drop(Id%%).ID
  701.   CASE 1, 2, 3 'Swords
  702.    Link.Sword = Drop(Id%%).ID
  703.   CASE 4 'bow
  704.    Link.Has_Bow = TRUE
  705.   CASE 5 'wand
  706.    Link.Has_Wand = TRUE
  707.   CASE 6 'Bait
  708.    Link.Has_Bait = TRUE
  709.   CASE 7, 8 'arrows
  710.    Link.Has_Arrow = Drop(Id%%).ID - 6 '1-arrow, 2-Silver arrow
  711.   CASE 9, 10 'boomerang
  712.    Link.Has_Boomerang = Drop(Id%%).ID - 8 '1-wood boomerang, 2-magic boomerang
  713.   CASE 11 'bombs
  714.    IF Drop(Id%%).Is_Special OR Drop(Id%%).Death = TRUE THEN '
  715.     Link.Max_Bombs = Link.Max_Bombs + 4
  716.    ELSE
  717.     Link.Has_Bombs = Link.Has_Bombs + 4
  718.     IF Link.Has_Bombs > Link.Max_Bombs THEN Link.Has_Bombs = Link.Max_Bombs
  719.    END IF
  720.   CASE 12, 13, 14 'letter, Blue and Red potions
  721.    Link.Has_Letter = Drop(Id%%).ID - 11
  722.   CASE 15 'dungeon maps
  723.   CASE 16 'magic book
  724.    Link.Has_Book = TRUE
  725.   CASE 17 'raft
  726.    Link.Has_Raft = TRUE
  727.   CASE 18
  728.    Link.Has_Ladder = TRUE
  729.   CASE 19, 20 'hearts
  730.    Link.Hearts = Link.Hearts + 2
  731.    IF Link.Hearts > Link.Containers * 2 THEN Link.Hearts = Link.Containers * 2
  732.   CASE 21 'heart container
  733.    Link.Containers = Link.Containers + 1
  734.   CASE 22, 23 'fairy
  735.   CASE 24 'stop watch
  736.   CASE 25 'Ruby x1
  737.    IF Link.Ruby + 1 <= 255 THEN Link.Ruby = Link.Ruby + 1
  738.   CASE 26 'Ruby x5
  739.    test% = Link.Ruby + 5
  740.    IF test% > 255 THEN Link.Ruby = 255 ELSE Link.Ruby = Link.Ruby + 5
  741.   CASE 27, 28 'Blue, Red Candle
  742.    Link.Has_Candle = Drop(Id%%).ID - 26
  743.   CASE 29, 30
  744.    Link.Has_Ring = Drop(Id%%).ID - 28
  745.   CASE 31
  746.    Link.Has_Shield = TRUE
  747.   CASE 32
  748.    Link.Has_Bracelet = TRUE
  749.   CASE 33
  750.    IF Link.Keys < 8 THEN Link.Keys = Link.Keys + 1
  751.   CASE 34
  752.    Link.Has_MagicKey = TRUE
  753.   CASE 35 'compass
  754.   CASE 36, 37 'triforce
  755.  Kill_Item G.Collected 'remove the item from the Drop array
  756.  IF NOT _SNDPLAYING(SFX(Items)) THEN G.Collected = -1
  757.  G.ItemCount = G.ItemCount - 1
  758.  Build_Item_Collision_layer
  759.  IF Link.World = UnderWorld THEN
  760.   X% = Offset_X(G.Scale_Factor)
  761.   Y% = Offset_Y(G.Scale_Factor)
  762.   _PUTIMAGE (X% + 16 * G.Scale_Factor + 64, Y% + 16 * G.Scale_Factor + 48)-STEP(352, 48), Layer(7), Layer(3), (290, 256)-STEP(15, 15)
  763.   _PUTIMAGE (X% + (16 * G.Scale_Factor * 7.5), Y% + (16 * G.Scale_Factor * 4))-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(3), (290, 256)-STEP(15, 15)
  764.  
  765. SUB Place_Link
  766.  STATIC Ftime AS _BYTE, Frame AS _BYTE
  767.  IF Link.Action = Walking THEN 'while Link is moving
  768.   G.Wtime = G.Wtime + 1 'Increment frame time
  769.   IF G.Wtime = 8 THEN
  770.    IF G.Wframe THEN G.Wframe = 0 ELSE G.Wframe = 1 'change frame
  771.    G.Wtime = 0 'reset frame time
  772.   END IF
  773.   _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 34 * Link.Direction + 17 * G.Wframe, 137)-STEP(15, 15)
  774.  ELSEIF Link.Action = Useing THEN 'when Link uses an item
  775.   Ftime = Ftime + 1 'Increment frame time
  776.   IF Ftime = 32 THEN Link.Action = None: Ftime = 0 'action is done
  777.   _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 17 * (Link.Direction + 8), 137)-STEP(15, 15)
  778.  ELSEIF Link.Action = GetItem THEN 'When Link gets a sword\item or buys something
  779.   'Held while music plays
  780.   _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 17 * 12, 137)-STEP(15, 15)
  781.  ELSEIF Link.Action = GetTriforce THEN 'When Link recovers a Triforce piece
  782.   'Held while music plays
  783.   _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 17 * 13, 137)-STEP(15, 15)
  784.  ELSEIF Link.Action = Attack THEN
  785.   G.Atime = G.Atime + 1 'Increment frame time
  786.   IF G.Atime = 4 THEN G.Aframe = G.Aframe + 1: G.Atime = 0 'change frame:reset frame time
  787.   Link_Attack
  788.  ELSE 'Link is standing Still(Action=None)
  789.   G.Atime = 0: G.Aframe = 0
  790.   G.Wtime = 0: G.Wframe = 0
  791.   _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 34 * Link.Direction + 17 * Frame%%, 137)-STEP(15, 15)
  792.  
  793. SUB Place_Player_Record (I%%, Where%)
  794.  SELECT CASE Where%
  795.   CASE 0
  796.    IF Records(I%% + 1).Beaten THEN _PUTIMAGE (260, 228 + 48 * I%%)-STEP(15, 31), Layer(12), Layer(16), (64, 247)-STEP(7, 15) 'Player 1 Sword
  797.    _PUTIMAGE (236, 234 + 48 * I%%)-STEP(31, 31), Layer(12), Layer(16), (1 + 17 * Records(I%% + 1).Ring, 230)-STEP(15, 15) 'Player 1 Link (green)
  798.    _PRINTSTRING (284, 232 + 48 * I%%), RTRIM$(Nick(I%% + 1)), Layer(16) 'player 1 name
  799.    _PRINTSTRING (284, 248 + 48 * I%%), LEFT$("   ", 3 - LEN(LTRIM$(RTRIM$(STR$(Records(I%% + 1).Played))))) + LTRIM$(STR$(Records(I%% + 1).Played)), Layer(16) 'player 1 tries
  800.    FOR j%% = 1 TO Records(I%% + 1).Containers
  801.     IF j%% < 4 THEN 'first 3 hearts are red
  802.      _PUTIMAGE (428 + 16 * j%%, 232 + 48 * I%%)-STEP(15, 15), Layer(12), Layer(16), (52, 230)-STEP(7, 7) 'Player 1 hearts red
  803.     ELSE
  804.      IF j%% < 9 THEN
  805.       _PUTIMAGE (428 + 16 * j%%, 232 + 48 * I%%)-STEP(15, 15), Layer(12), Layer(16), (73, 267)-STEP(7, 7) 'Player 1 hearts white
  806.      ELSE
  807.       _PUTIMAGE (428 + 16 * (j%% - 8), 232 + 64 * I%%)-STEP(15, 15), Layer(12), Layer(16), (73, 267)-STEP(7, 7) 'Player 1 hearts white
  808.      END IF
  809.     END IF
  810.    NEXT j%%
  811.   CASE 1
  812.    IF Records(I%% + 1).Beaten THEN _PUTIMAGE (326, 148 + 48 * (I%% - 1))-STEP(15, 31), Layer(12), Layer(16), (64, 247)-STEP(7, 15) 'Player 1 Sword
  813.    _PUTIMAGE (300, 154 + 48 * (I%%))-STEP(31, 31), Layer(12), Layer(16), (1 + 17 * Records(I%% + 1).Ring, 230)-STEP(15, 15) 'Player 1 Link (green)
  814.    _PRINTSTRING (364, 152 + 48 * (I%%)), RTRIM$(Nick(I%% + 1)), Layer(1)
  815.  
  816.  
  817. SUB Place_Tile_On_Screen (X%, Y%, Tile~%%, L&)
  818.  Gy% = 17 * (Tile~%% \ 20) 'get which row it comes from
  819.  Gx% = 17 * (Tile~%% MOD 20) 'which column position
  820.  _PUTIMAGE (Offset_X(G.Scale_Factor) + X%, Offset_Y(G.Scale_Factor) + Y%)-STEP(G.Scale_X, G.Scale_Y), Layer(7), L&, (1 + Gx%, 1 + Gy%)-STEP(15, 15)
  821.  
  822. SUB Scroll_Screen_II (Dir%%)
  823.  Cx% = Offset_X(G.Scale_Factor) 'top left corner location of map displayed
  824.  Cy% = Offset_Y(G.Scale_Factor)
  825.  Sfx% = 16 * 16 * G.Scale_Factor 'size of the map display
  826.  Sfy% = 11 * 16 * G.Scale_Factor
  827.  Lwx% = 16 * 16 * G.Scale_Factor * Link.World_X 'map area link is in
  828.  Lwy% = 11 * 16 * G.Scale_Factor * Link.World_Y
  829.  SELECT CASE Dir%%
  830.   CASE Up
  831.    FOR y% = 0 TO Sfy% STEP 2 * G.Scale_Factor
  832.     _PUTIMAGE (Cx%, Cy%)-STEP(Sfx%, Sfy%), Layer(6), Layer(1), (Lwx%, Lwy% - y%)-STEP(Sfx%, Sfy%)
  833.     IF y% > 16 * G.Scale_Factor THEN Link.Screen_Y = Link.Screen_Y + 2 * G.Scale_Factor
  834.     Place_Link
  835.     _LIMIT 60
  836.     _PUTIMAGE , Layer(1), Layer(0)
  837.    NEXT
  838.    Link.World_Y = Link.World_Y - 1
  839.   CASE Down
  840.    FOR y% = 0 TO Sfy% STEP 2 * G.Scale_Factor
  841.     _PUTIMAGE (Cx%, Cy%)-STEP(Sfx%, Sfy%), Layer(6), Layer(1), (Lwx%, Lwy% + y%)-STEP(Sfx%, Sfy%)
  842.     IF y% > 16 * G.Scale_Factor THEN Link.Screen_Y = Link.Screen_Y - 2 * G.Scale_Factor
  843.     Place_Link
  844.     _LIMIT 60
  845.     _PUTIMAGE , Layer(1), Layer(0)
  846.    NEXT
  847.    Link.World_Y = Link.World_Y + 1
  848.   CASE Left
  849.    FOR x% = 0 TO Sfx% STEP 2 * G.Scale_Factor
  850.     _PUTIMAGE (Cx%, Cy%)-STEP(Sfx%, Sfy%), Layer(6), Layer(1), (Lwx% - x%, Lwy%)-STEP(Sfx%, Sfy%)
  851.     IF x% > 16 * G.Scale_Factor THEN Link.Screen_X = Link.Screen_X + 2 * G.Scale_Factor
  852.     Place_Link
  853.     _LIMIT 60
  854.     _PUTIMAGE , Layer(1), Layer(0)
  855.    NEXT
  856.    Link.World_X = Link.World_X - 1
  857.   CASE Right
  858.    FOR x% = 0 TO Sfx% STEP 2 * G.Scale_Factor
  859.     _PUTIMAGE (Cx%, Cy%)-STEP(Sfx%, Sfy%), Layer(6), Layer(1), (Lwx% + x%, Lwy%)-STEP(Sfx%, Sfy%)
  860.     IF x% > 16 * G.Scale_Factor THEN Link.Screen_X = Link.Screen_X - 2 * G.Scale_Factor
  861.     Place_Link
  862.     _LIMIT 60
  863.     _PUTIMAGE , Layer(1), Layer(0)
  864.    NEXT
  865.    Link.World_X = Link.World_X + 1
  866.  Place_Link
  867.  Lwx% = 16 * 16 * G.Scale_Factor * Link.World_X
  868.  Lwy% = 11 * 16 * G.Scale_Factor * Link.World_Y
  869.  _PUTIMAGE (Cx%, Cy%)-STEP(Sfx%, Sfy%), Layer(6), Layer(3), (Lwx%, Lwy%)-STEP(Sfx%, Sfy%) 'move new screen to layer(3)
  870.  
  871. SUB Shift_New_Map_Screen
  872.  SELECT CASE G.NextScreen
  873.   CASE Up
  874.    Scroll_Screen_II Up
  875.   CASE Down
  876.    Scroll_Screen_II Down
  877.   CASE Left
  878.    Scroll_Screen_II Left
  879.   CASE Right
  880.    Scroll_Screen_II Right
  881.  G.NextScreen = -1
  882.  
  883. SUB Title_Screen
  884.  _CLEARCOLOR _RGB32(21), Layer(9)
  885.  _CLEARCOLOR _RGB32(21), Layer(10)
  886.  _SNDVOL BGM(0), .33
  887.  _SNDLOOP BGM(0)
  888.  DO
  889.   F%% = 0: F% = 0: ExitFlag%% = FALSE
  890.   DO: _LIMIT 60: LOOP WHILE _SNDGETPOS(BGM(0)) > 10
  891.   DO
  892.    _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (0, 0)-STEP(255, 223)
  893.    SELECT CASE F%%
  894.     CASE 12 TO 18
  895.      _PUTIMAGE (400 - 74, 300 - 128)-STEP(143, 143), Layer(9), Layer(1), (289, 0)-STEP(71, 71)
  896.     CASE 19 TO 24
  897.      _PUTIMAGE (400 - 74, 300 - 128)-STEP(143, 143), Layer(9), Layer(1), (289, 0 + 72)-STEP(71, 71)
  898.     CASE 25 TO 36
  899.      _PUTIMAGE (400 - 74, 300 - 128)-STEP(143, 143), Layer(9), Layer(1), (289, 0 + 144)-STEP(71, 71)
  900.     CASE 37 TO 52
  901.      _PUTIMAGE (400 - 74, 300 - 128)-STEP(143, 143), Layer(9), Layer(1), (289, 0 + 72)-STEP(71, 71)
  902.     CASE 53 TO 59
  903.      _PUTIMAGE (400 - 74, 300 - 128)-STEP(143, 143), Layer(9), Layer(1), (289, 0 + 0)-STEP(71, 71)
  904.    END SELECT
  905.    _PUTIMAGE (304, 523 - 112), Layer(10), Layer(1), (0 + 64 * wave%%, 0)-STEP(63, 111)
  906.    _PUTIMAGE , Layer(1), Layer(0)
  907.    _LIMIT 90
  908.    F%% = F%% + 1
  909.    IF F%% = 60 THEN F%% = 0
  910.    IF F%% MOD 2 = 0 THEN wave%% = wave%% + 1
  911.    IF wave%% = 16 THEN wave%% = 0
  912.    IF Get_Input = START_BUTTON THEN ExitFlag%% = TRUE
  913.   LOOP UNTIL _SNDGETPOS(BGM(0)) > 8.4 OR ExitFlag%%
  914.  
  915.   IF NOT ExitFlag%% THEN
  916.    'start the title fade
  917.    _DEST Layer(1)
  918.    F%% = 0
  919.    DO
  920.     SELECT CASE F%
  921.      CASE 0 TO 13
  922.       LINE (144, 76)-STEP(511, 447), _RGB32(202, 241, 159), BF
  923.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (361, 0)-STEP(255, 223)
  924.      CASE 13 TO 24
  925.       LINE (144, 76)-STEP(511, 447), _RGB32(182, 216, 255), BF
  926.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (361, 0)-STEP(255, 223)
  927.      CASE 25 TO 34
  928.       LINE (144, 76)-STEP(511, 447), _RGB32(166, 229, 255), BF
  929.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (361, 0)-STEP(255, 223)
  930.      CASE 35 TO 42
  931.       LINE (144, 76)-STEP(511, 447), _RGB32(165, 238, 223), BF
  932.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (617, 0)-STEP(255, 223)
  933.      CASE 43 TO 48
  934.       LINE (144, 76)-STEP(511, 447), _RGB32(37, 190, 255), BF
  935.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (617, 0)-STEP(255, 223)
  936.      CASE 49 TO 52
  937.       LINE (144, 76)-STEP(511, 447), _RGB32(0, 109, 181), BF
  938.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (617, 0)-STEP(255, 223)
  939.      CASE 53 TO 57
  940.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (873, 0)-STEP(255, 223)
  941.       Wf% = 112
  942.      CASE 58 TO 61
  943.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (1129, 0)-STEP(255, 223)
  944.       Wf% = 224
  945.      CASE 62 TO 363
  946.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (1385, 0)-STEP(255, 223)
  947.       Wf% = 336
  948.      CASE 364 TO 385
  949.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (1641, 0)-STEP(255, 223)
  950.       Wf% = 448
  951.      CASE 386 TO 393
  952.       _PUTIMAGE ((800 - 512) \ 2, (600 - 448) \ 2)-STEP(511, 447), Layer(9), Layer(1), (1897, 0)-STEP(255, 223)
  953.       Wf% = 560
  954.     END SELECT
  955.     _PUTIMAGE (304, 523 - 112), Layer(10), Layer(1), (0 + 64 * wave%%, 0 + Wf%)-STEP(63, 111)
  956.     _PUTIMAGE , Layer(1), Layer(0)
  957.     _LIMIT 90
  958.     F% = F% + 1
  959.     IF F% = 394 THEN ExitFlag%% = TRUE
  960.     IF F% MOD 2 = 0 THEN wave%% = wave%% + 1
  961.     IF wave%% = 16 THEN wave%% = 0
  962.     IF Get_Input = START_BUTTON THEN ExitFlag%% = TRUE
  963.    LOOP UNTIL ExitFlag%%
  964.    ExitFlag%% = FALSE
  965.   END IF
  966.   ClearLayer Layer(0)
  967.   ClearLayer Layer(1)
  968.   IF NOT ExitFlag%% THEN
  969.    'Title scroll
  970.    DO: _LIMIT 60: IF Get_Input = START_BUTTON THEN ExitFlag%% = TRUE
  971.    LOOP UNTIL _SNDGETPOS(BGM(0)) > 16 OR ExitFlag%%
  972.    F% = 0
  973.    IF NOT ExitFlag%% THEN
  974.     DO
  975.      SELECT CASE F%
  976.       CASE 0 TO 223
  977.        _PUTIMAGE (144, 76)-STEP(511, 447), Layer(11), Layer(1), (0, -223 + F%)-STEP(255, 223)
  978.       CASE IS >= 354
  979.        IF blink% THEN
  980.         _PUTIMAGE (72, 332)-STEP(7, 15), Layer(7), Layer(11), (247, 154)-STEP(7, 15) 'heart
  981.         _PUTIMAGE (72, 464)-STEP(7, 15), Layer(7), Layer(11), (213, 171)-STEP(7, 15) 'Ruby
  982.         _PUTIMAGE (120, 1432)-STEP(15, 15), Layer(7), Layer(11), (239, 188)-STEP(15, 15) 'Triforce
  983.        ELSE
  984.         _PUTIMAGE (72, 332)-STEP(7, 15), Layer(7), Layer(11), (239, 154)-STEP(7, 15) 'heart
  985.         _PUTIMAGE (72, 464)-STEP(7, 15), Layer(7), Layer(11), (205, 171)-STEP(7, 15) 'Ruby
  986.         _PUTIMAGE (120, 1432)-STEP(15, 15), Layer(7), Layer(11), (222, 188)-STEP(15, 15) 'Triforce
  987.        END IF
  988.        IF Fblink% THEN
  989.         _PUTIMAGE (72, 400)-STEP(7, 15), Layer(7), Layer(11), (256, 188)-STEP(7, 15) 'fairy blank
  990.         _PUTIMAGE (72, 400)-STEP(7, 15), Layer(7), Layer(11), (281, 154)-STEP(7, 15) 'fairy
  991.        ELSE
  992.         _PUTIMAGE (72, 400)-STEP(7, 15), Layer(7), Layer(11), (256, 188)-STEP(7, 15) 'fairy blank
  993.         _PUTIMAGE (72, 400)-STEP(7, 15), Layer(7), Layer(11), (273, 154)-STEP(7, 15) 'fairy
  994.        END IF
  995.        _PUTIMAGE (144, 76)-STEP(511, 447), Layer(11), Layer(1), (0, F% - 354)-STEP(255, 223)
  996.      END SELECT
  997.      _PUTIMAGE , Layer(1), Layer(0)
  998.      _LIMIT 30
  999.      F% = F% + 1
  1000.      IF F% = 1762 THEN F% = F% - 1 'ExitFlag%% = TRUE
  1001.      B% = B% + 1
  1002.      IF B% = 4 THEN blink% = NOT blink%: B% = 0
  1003.      IF B% MOD 2 = 0 THEN Fblink% = NOT Fblink%
  1004.      IF Get_Input = START_BUTTON THEN ExitFlag%% = TRUE
  1005.     LOOP UNTIL _SNDGETPOS(BGM(0)) >= 79.75 OR ExitFlag%%
  1006.     ExitFlag%% = FALSE
  1007.    END IF
  1008.   END IF
  1009.  
  1010.  LOOP UNTIL ExitFlag%%
  1011.  _DEST Layer(0)
  1012.  _SNDSTOP BGM(0)
  1013.  DO: LOOP UNTIL Get_Input%% = -1
  1014.  
  1015.  
  1016. FUNCTION Projectile_Collision%%
  1017.  Result%% = FALSE
  1018.  FOR i%% = 0 TO G.Projectile_Count
  1019.  Projectile_Collision = Result%%
  1020.  
  1021.  
  1022. SUB Walk_InOut (Which%%)
  1023.  STATIC Shrink%%, Entry_X, Entry_Y
  1024.  SELECT CASE G.Scale_Factor
  1025.   CASE 1
  1026.    MoveY%% = 2
  1027.   CASE 2
  1028.    MoveY%% = 5
  1029.   CASE 3
  1030.    MoveY%% = 8
  1031.  
  1032.  SELECT CASE Which%%
  1033.   CASE GO_IN
  1034.    Link.Screen_X = 16 * G.Scale_Factor * (Link.Tile_X - Link.World_X * 16) + Offset_X(G.Scale_Factor)
  1035.    FOR y% = 0 TO MoveY%% 'move link into the cave opening
  1036.     _PUTIMAGE , Layer(3), Layer(1) 'place map image
  1037.     G.Wtime = G.Wtime + 1 'Increment frame time
  1038.     IF G.Wtime >= 2 THEN
  1039.      IF G.Wframe THEN G.Wframe = 0 ELSE G.Wframe = 1 'change frame
  1040.      G.Wtime = 0 'reset frame time
  1041.     END IF
  1042.     Link.Screen_Y = Link.Screen_Y - 2
  1043.     _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 34 * Link.Direction + 17 * G.Wframe, 137)-STEP(15, 15)
  1044.     _PUTIMAGE , Layer(1), Layer(0)
  1045.     _DELAY .033
  1046.    NEXT y%
  1047.  
  1048.    _SNDPLAY SFX(Stairs)
  1049.    DO 'have link walk `down` into the cave
  1050.     _PUTIMAGE , Layer(3), Layer(1) 'place map image
  1051.  
  1052.     G.Wtime = G.Wtime + 1 'Increment frame time
  1053.     IF G.Wtime = 8 THEN
  1054.      Shrink%% = Shrink%% + 2
  1055.      Link.Screen_Y = Link.Screen_Y + 2 * G.Scale_Factor
  1056.      IF G.Wframe THEN G.Wframe = 0 ELSE G.Wframe = 1 'change frame
  1057.      G.Wtime = 0 'reset frame time
  1058.     END IF
  1059.     _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y - Shrink%% * G.Scale_Factor), Layer(7), Layer(1), (1 + 34 * Link.Direction + 17 * G.Wframe, 137)-STEP(15, 15 - Shrink%%)
  1060.     _PUTIMAGE , Layer(1), Layer(0)
  1061.     _LIMIT 45
  1062.    LOOP WHILE _SNDPLAYING(SFX(Stairs))
  1063.    Entry_X = Link.Screen_X
  1064.    Entry_Y = Link.Screen_Y
  1065.    Link.Screen_X = Offset_X(G.Scale_Factor) + 16 * G.Scale_Factor * 7.5
  1066.    Link.Screen_Y = Offset_Y(G.Scale_Factor) + 16 * G.Scale_Factor * 9
  1067.    TIMER(TFX(0)) ON
  1068.   CASE GO_OUT
  1069.    TIMER(TFX(0)) OFF
  1070.    Build_Map_Screen 16 * Link.World_X, 11 * Link.World_Y
  1071.    Link.Screen_X = Entry_X 'Offset_X(G.Scale_Factor) + 16 * G.Scale_Factor * (Link.Tile_X - Link.World_X * 16)
  1072.    Link.Screen_Y = Entry_Y 'Offset_Y(G.Scale_Factor) + 16 * G.Scale_Factor * (Link.Tile_Y - Link.World_Y * 11)
  1073.    _SNDPLAY SFX(Stairs)
  1074.    DO 'have link walk `down` into the cave
  1075.     _PUTIMAGE , Layer(3), Layer(1) 'place map image
  1076.  
  1077.     G.Wtime = G.Wtime + 1 'Increment frame time
  1078.     IF G.Wtime = 8 THEN
  1079.      Shrink%% = Shrink%% - 2
  1080.      Link.Screen_Y = Link.Screen_Y - 2 * G.Scale_Factor
  1081.      IF G.Wframe THEN G.Wframe = 0 ELSE G.Wframe = 1 'change frame
  1082.      G.Wtime = 0 'reset frame time
  1083.     END IF
  1084.     _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y - Shrink%% * G.Scale_Factor), Layer(7), Layer(1), (1 + 34 * Link.Direction + 17 * G.Wframe, 137)-STEP(15, 15 - Shrink%%)
  1085.  
  1086.     _PUTIMAGE , Layer(1), Layer(0)
  1087.     _LIMIT 45
  1088.    LOOP WHILE _SNDPLAYING(SFX(Stairs))
  1089.  
  1090.    FOR y% = 0 TO 16 'move link into the cave opening
  1091.     _PUTIMAGE , Layer(3), Layer(1) 'place map image
  1092.     G.Wtime = G.Wtime + 1 'Increment frame time
  1093.     IF G.Wtime >= 2 THEN
  1094.      IF G.Wframe THEN G.Wframe = 0 ELSE G.Wframe = 1 'change frame
  1095.      G.Wtime = 0 'reset frame time
  1096.     END IF
  1097.     Link.Screen_Y = Link.Screen_Y + 2
  1098.     _PUTIMAGE (Link.Screen_X, Link.Screen_Y)-STEP(G.Scale_X, G.Scale_Y), Layer(7), Layer(1), (1 + 34 * Link.Direction + 17 * G.Wframe, 137)-STEP(15, 15)
  1099.     _PUTIMAGE , Layer(1), Layer(0)
  1100.     _DELAY .016
  1101.    NEXT y%
  1102.  
  1103.    Link.World = OverWorld
  1104.    Link.Tile_Y = Link.Tile_Y + 1
  1105.    Shrink%% = 0
  1106.  
  1107. SUB Build_Custom_Control_Screen (L&())
  1108.  'setup custom controls screen
  1109.  _PUTIMAGE (120, 72), Layer(18), L&(2)
  1110.  _DEST L&(2)
  1111.  'up arrow
  1112.  LINE (208, 168)-(112, 172), _RGB32(255, 127, 0), BF
  1113.  LINE (112, 172)-(116, 500), _RGB32(255, 127, 0), BF
  1114.  LINE (112, 500)-(292, 504), _RGB32(255, 127, 0), BF
  1115.  'left arrow
  1116.  LINE (172, 200)-(128, 204), _RGB32(92, 192, 0), BF
  1117.  LINE (128, 204)-(132, 456), _RGB32(92, 192, 0), BF
  1118.  LINE (128, 456)-(320, 460), _RGB32(92, 192, 0), BF
  1119.  'down arrow
  1120.  LINE (208, 252)-(144, 256), _RGB32(0, 127, 255), BF
  1121.  LINE (144, 256)-(148, 412), _RGB32(0, 127, 255), BF
  1122.  LINE (144, 412)-(336, 416), _RGB32(0, 127, 255), BF
  1123.  'right arrow
  1124.  LINE (256, 224)-(260, 280), _RGB32(0, 64, 160), BF
  1125.  LINE (160, 280)-(260, 284), _RGB32(0, 64, 160), BF
  1126.  LINE (160, 284)-(164, 366), _RGB32(0, 64, 160), BF
  1127.  LINE (160, 366)-(340, 370), _RGB32(0, 64, 160), BF
  1128.  'select
  1129.  LINE (344, 252)-(348, 500), _RGB32(255, 64, 0), BF
  1130.  LINE (344, 500)-(516, 504), _RGB32(255, 64, 0), BF
  1131.  'start
  1132.  LINE (408, 252)-(412, 456), _RGB32(212, 0, 16), BF
  1133.  LINE (408, 456)-(576, 460), _RGB32(212, 0, 16), BF
  1134.  'B Button
  1135.  LINE (506, 248)-(510, 296), _RGB32(64, 92, 212), BF
  1136.  LINE (420, 296)-(510, 300), _RGB32(64, 92, 212), BF
  1137.  LINE (420, 300)-(424, 416), _RGB32(64, 92, 212), BF
  1138.  LINE (420, 412)-(576, 416), _RGB32(64, 92, 212), BF
  1139.  'A Button
  1140.  LINE (576, 248)-(580, 312), _RGB32(80, 64, 180), BF
  1141.  LINE (436, 312)-(580, 316), _RGB32(80, 64, 180), BF
  1142.  LINE (436, 312)-(440, 368), _RGB32(80, 64, 180), BF
  1143.  LINE (436, 368)-(608, 372), _RGB32(80, 64, 180), BF
  1144.  'setup click layer  40,32 offset
  1145.  _DEST L&(3)
  1146.  LINE (200, 164)-STEP(35, 30), _RGB(1, 0, 0), BF 'up
  1147.  LINE (200, 230)-STEP(35, 30), _RGB(2, 0, 0), BF 'down
  1148.  LINE (168, 198)-STEP(35, 30), _RGB(3, 0, 0), BF 'left
  1149.  LINE (236, 198)-STEP(35, 30), _RGB(4, 0, 0), BF 'right
  1150.  
  1151.  LINE (318, 232)-STEP(45, 20), _RGB(5, 0, 0), BF 'Select
  1152.  LINE (388, 232)-STEP(45, 20), _RGB(6, 0, 0), BF 'Start
  1153.  
  1154.  LINE (478, 212)-STEP(58, 60), _RGB(7, 0, 0), BF 'Button A
  1155.  LINE (548, 212)-STEP(58, 60), _RGB(8, 0, 0), BF 'Button B
  1156.  
  1157.  _PRINTSTRING (96, 0), "Click on the controller button you wish to customize, Press the new key.", L&(2)
  1158.  _PRINTSTRING (144, 16), "Tab to switch Control type. When done click EXIT when done.", L&(2)
  1159.  _PRINTSTRING (96, 32), "With a JOYPAD: TYPE I-use axis for movement, TYPE II-use buttons to move.", L&(2)
  1160.  _DEST L&(0)
  1161.  
  1162. SUB Custom_Controls
  1163.  OldD& = _DEST
  1164.  OldS& = _SOURCE
  1165.  DIM Clayer(4) AS LONG
  1166.  Clayer(0) = _DISPLAY
  1167.  Clayer(1) = _NEWIMAGE(800, 600, 32)
  1168.  Clayer(2) = _NEWIMAGE(800, 600, 32)
  1169.  Clayer(3) = _NEWIMAGE(800, 600, 32)
  1170.  ClearLayer Clayer(2)
  1171.  ClearLayerTrans Clayer(4)
  1172.  Build_Custom_Control_Screen Clayer()
  1173.  _FONT FFX(0), Clayer(1)
  1174.  _FONT FFX(0), Clayer(4)
  1175.  _DEST Clayer(1)
  1176.  _SOURCE Clayer(3)
  1177.  DO
  1178.  
  1179.   KBD& = _KEYHIT
  1180.   IF KBD& < 0 THEN KBD& = 0 'handle button up `-` codes
  1181.  
  1182.   IF (NOT G.ControlType) AND Master%% THEN
  1183.    Nul%% = _DEVICEINPUT(C.Control_Pad)
  1184.    IF NOT C.BAD_Pad THEN
  1185.     IF Master%% > 4 THEN
  1186.      FOR i%% = 1 TO _LASTBUTTON(C.Control_Pad)
  1187.       IF _BUTTON(i%%) THEN Bselect%% = i%%: i%% = _LASTBUTTON(C.Control_Pad) + 1
  1188.      NEXT i%%
  1189.     ELSE
  1190.      FOR i%% = 1 TO _LASTAXIS(C.Control_Pad)
  1191.       test%% = _AXIS(i%%)
  1192.       IF test%% THEN Aselect%% = i%%: i%% = _LASTAXIS(C.Control_Pad)
  1193.      NEXT i%%
  1194.     END IF
  1195.    ELSE
  1196.     FOR i%% = 1 TO _LASTBUTTON(C.Control_Pad)
  1197.      IF _BUTTON(i%%) THEN Bselect%% = i%%: i%% = _LASTBUTTON(C.Control_Pad) + 1
  1198.     NEXT i%%
  1199.    END IF
  1200.   END IF
  1201.  
  1202.   _PUTIMAGE , Clayer(2), Clayer(1)
  1203.   Nul%% = _MOUSEINPUT
  1204.   X% = _MOUSEX: Y% = _MOUSEY
  1205.   Selection%% = _RED32(POINT(X%, Y%)) 'is mouse over anything?
  1206.  
  1207.   IF Master%% THEN Selection%% = Master%% 'keep selection locked until key press
  1208.  
  1209.    IF NOT Master%% THEN Master%% = Selection%% 'if no Master selection set then set it.
  1210.    IF ExitSet%% THEN ExitFlag%% = TRUE
  1211.    IF X% > 640 AND X% < 770 AND Y% > 440 AND Y% < 475 THEN C.BAD_Pad = NOT C.BAD_Pad 'toggle controller directional type
  1212.   END IF
  1213.  
  1214.   IF Master%% AND G.ControlType THEN 'once player clicks and controller is Keyboard
  1215.    IF KBD& <> ESC_Key THEN
  1216.     Set_KeyBoard_Control Master%%, KBD&
  1217.    END IF
  1218.   ELSEIF Master%% AND (NOT G.ControlType) THEN 'once player clicks and controller is Joy Pad
  1219.    SELECT CASE Master%%
  1220.     CASE 1 'Up arrow
  1221.      IF Aselect%% THEN
  1222.       C.Joy_Up = Aselect%%
  1223.       IF test%% < 0 THEN C.Joy_Up_Val = TRUE ELSE C.Joy_Up_Val = 1
  1224.       Master%% = FALSE 'unlock selection with button press
  1225.       Aselect%% = 0
  1226.      ELSEIF Bselect%% AND C.BAD_Pad THEN
  1227.       C.Joy_Button_Up = Bselect%%
  1228.       Master%% = FALSE 'unlock selection with button press
  1229.       Bselect%% = 0
  1230.      END IF
  1231.     CASE 2 'Down arrow
  1232.      IF Aselect%% THEN
  1233.       C.Joy_Down = Aselect%%
  1234.       IF test%% < 0 THEN C.Joy_Down_Val = TRUE ELSE C.Joy_Down_Val = 1
  1235.       Master%% = FALSE 'unlock selection with button press
  1236.       Aselect%% = 0
  1237.      ELSEIF Bselect%% AND C.BAD_Pad THEN
  1238.       C.Joy_Button_Down = Bselect%%
  1239.       Master%% = FALSE 'unlock selection with button press
  1240.       Bselect%% = 0
  1241.      END IF
  1242.     CASE 3 'left arrow
  1243.      IF Aselect%% THEN
  1244.       C.Joy_Left = Aselect%%
  1245.       IF test%% < 0 THEN C.Joy_Left_Val = TRUE ELSE C.Joy_Left_Val = 1
  1246.       Master%% = FALSE 'unlock selection with button press
  1247.       Aselect%% = 0
  1248.      ELSEIF Bselect%% AND C.BAD_Pad THEN
  1249.       C.Joy_Button_Left = Bselect%%
  1250.       Master%% = FALSE 'unlock selection with button press
  1251.       Bselect%% = 0
  1252.      END IF
  1253.     CASE 4 'right arrow
  1254.      IF Aselect%% THEN
  1255.       C.Joy_Right = Aselect%%
  1256.       IF test%% < 0 THEN C.Joy_Right_Val = TRUE ELSE C.Joy_Right_Val = 1
  1257.       Master%% = FALSE 'unlock selection with button press
  1258.       Aselect%% = 0
  1259.      ELSEIF Bselect%% AND C.BAD_Pad THEN
  1260.       C.Joy_Button_Right = Bselect%%
  1261.       Master%% = FALSE 'unlock selection with button press
  1262.       Bselect%% = 0
  1263.      END IF
  1264.     CASE 5 'select
  1265.      IF Bselect%% THEN C.Joy_Select = Bselect%%: Master%% = FALSE 'unlock selection with button press
  1266.      Bselect%% = 0
  1267.     CASE 6 'start
  1268.      IF Bselect%% THEN C.Joy_Start = Bselect%%: Master%% = FALSE 'unlock selection with button press
  1269.      Bselect%% = 0
  1270.     CASE 7 'b
  1271.      IF Bselect%% THEN C.Joy_B_Button = Bselect%%: Master%% = FALSE 'unlock selection with button press
  1272.      Bselect%% = 0
  1273.     CASE 8 'a
  1274.      IF Bselect%% THEN C.Joy_A_Button = Bselect%%: Master%% = FALSE 'unlock selection with button press
  1275.      Bselect%% = 0
  1276.    END SELECT
  1277.  
  1278.   END IF
  1279.  
  1280.   IF KBD& = 9 THEN G.ControlType = NOT G.ControlType ': LINE (128, 350)-STEP(496, 160), _RGB32(0), BF
  1281.   IF (NOT G.ControlType) AND C.Control_Pad = 0 THEN 'setup joy pad
  1282.    IF G.Device_Count <= 2 THEN 'is there a joy pad to use?
  1283.     _PUTIMAGE (188, 100)-STEP(384, 48), Layer(12), Clayer(0), (86, 240)-STEP(7, 7) 'black out name
  1284.     _PRINTSTRING (192, 108), " NO OTHER DEVICE FOUND", Clayer(0)
  1285.     _PRINTSTRING (288, 126), " PRESS TAB", Clayer(0)
  1286.     _KEYCLEAR
  1287.     DO: _LIMIT 30: LOOP UNTIL INKEY$ = CHR$(9)
  1288.     _KEYCLEAR
  1289.     G.ControlType = TRUE
  1290.    ELSE
  1291.     Select_Device
  1292.    END IF
  1293.   END IF
  1294.  
  1295.   'Screen Updating
  1296.   Display_Keys G.ControlType
  1297.   Highlite_Select Selection%%
  1298.   ExitSet%% = Display_Exit_Controls(X%, Y%)
  1299.   IF NOT G.ControlType THEN Display_BAD_Controller_Option
  1300.   '---------------------------------------------------------------------------------------------------------
  1301.   '  _PUTIMAGE , Clayer(4), Clayer(1)
  1302.   _PUTIMAGE , Clayer(1), Clayer(0)
  1303.   _LIMIT 30
  1304.   IF (NOT G.ControlType) THEN DO: LOOP WHILE _DEVICEINPUT(C.Control_Pad)
  1305.  
  1306.  LOOP UNTIL ExitFlag%%
  1307.  
  1308.  _DEST OldD&
  1309.  _SOURCE OldS&
  1310.  
  1311. SUB Select_Device
  1312.  'removing the first 2 devices, assuming keyboard=1 and mouse=2
  1313.  IF G.Device_Count > 3 THEN
  1314.   DO
  1315.    KBD$ = INKEY$
  1316.    _PUTIMAGE , Layer(16), Layer(1)
  1317.    '   Draw_Window 4, 7, 30, 1 + (G.Device_Count - 2) * 2, Layer(1)
  1318.    FOR i%% = 3 TO G.Device_Count
  1319.     temp$ = Device_Name$(i%%)
  1320.     _PRINTSTRING (16 * 12 - LEN(temp$) \ 2, 16 * (8 + (i%% - 3))), LTRIM$(STR$(i%%)) + "-" + temp$
  1321.    NEXT i%%
  1322.    _PRINTSTRING (16 * 6 + 8, 16 * (8 + (i%% - 3)) + 2), "Press Number of controller"
  1323.    _PUTIMAGE , Layer(1), Layer(0)
  1324.    _LIMIT 30
  1325.    IF VAL(KBD$) > 2 AND VAL(KBD$) <= G.Device_Count THEN C.Control_Pad = VAL(KBD$): ExitFlag%% = TRUE
  1326.    IF KBD$ = CHR$(27) THEN CLOSE: END
  1327.   LOOP UNTIL ExitFlag%%
  1328.  ELSE 'if there is only 1 control device to choose from auto assign
  1329.   C.Control_Pad = 3
  1330.  
  1331.  
  1332. SUB Display_Keys (ID%%)
  1333.  IF ID%% THEN
  1334.   _PRINTSTRING (312, 116), "Keyboard"
  1335.   _PRINTSTRING (144, 480), ControlName(C.KBCon_Up)
  1336.   _PRINTSTRING (176, 392), ControlName(C.KBCon_Down)
  1337.   _PRINTSTRING (160, 436), ControlName(C.KBCon_Left)
  1338.   _PRINTSTRING (192, 348), ControlName(C.KBCon_Right)
  1339.   _PRINTSTRING (468, 348), ControlName(C.KBCon_A_Button)
  1340.   _PRINTSTRING (450, 392), ControlName(C.KBCon_B_Button)
  1341.   _PRINTSTRING (376, 480), ControlName(C.KBCon_Select)
  1342.   _PRINTSTRING (444, 436), ControlName(C.KBCon_Start)
  1343.   _PRINTSTRING (312, 116), "JoyPad-" + LTRIM$(STR$(C.Control_Pad))
  1344.   IF NOT C.BAD_Pad THEN
  1345.    IF C.Joy_Up = -22 THEN
  1346.     _PRINTSTRING (144, 480), "UnDefined"
  1347.    ELSE
  1348.     IF C.Joy_Up_Val < 0 THEN a$ = " -" ELSE a$ = " +"
  1349.     _PRINTSTRING (144, 480), "Axis:" + STR$(C.Joy_Up) + a$
  1350.    END IF
  1351.    IF C.Joy_Down = -22 THEN
  1352.     _PRINTSTRING (176, 392), "UnDefined"
  1353.    ELSE
  1354.     IF C.Joy_Down_Val < 0 THEN a$ = " -" ELSE a$ = " +"
  1355.     _PRINTSTRING (176, 392), "Axis:" + STR$(C.Joy_Down) + a$
  1356.    END IF
  1357.    IF C.Joy_Left = -22 THEN
  1358.     _PRINTSTRING (160, 436), "UnDefined"
  1359.    ELSE
  1360.     IF C.Joy_Left_Val < 0 THEN a$ = " -" ELSE a$ = " +"
  1361.     _PRINTSTRING (160, 436), "Axis:" + STR$(C.Joy_Left) + a$
  1362.    END IF
  1363.    IF C.Joy_Right = -22 THEN
  1364.     _PRINTSTRING (192, 348), "UnDefined"
  1365.    ELSE
  1366.     IF C.Joy_Right_Val < 0 THEN a$ = " -" ELSE a$ = " +"
  1367.     _PRINTSTRING (192, 348), "Axis:" + STR$(C.Joy_Right) + a$
  1368.    END IF
  1369.   ELSE
  1370.    IF C.Joy_Button_Up = -22 THEN
  1371.     _PRINTSTRING (144, 480), "UnDefined"
  1372.    ELSE
  1373.     _PRINTSTRING (144, 480), "Button:" + STR$(C.Joy_Button_Up) + a$
  1374.    END IF
  1375.    IF C.Joy_Button_Down = -22 THEN
  1376.     _PRINTSTRING (176, 392), "UnDefined"
  1377.    ELSE
  1378.     _PRINTSTRING (176, 392), "Button:" + STR$(C.Joy_Button_Down) + a$
  1379.    END IF
  1380.    IF C.Joy_Button_Left = -22 THEN
  1381.     _PRINTSTRING (160, 436), "UnDefined"
  1382.    ELSE
  1383.     _PRINTSTRING (160, 436), "Button:" + STR$(C.Joy_Button_Left) + a$
  1384.    END IF
  1385.    IF C.Joy_Button_Right = -22 THEN
  1386.     _PRINTSTRING (192, 348), "UnDefined"
  1387.    ELSE
  1388.     _PRINTSTRING (192, 348), "Button:" + STR$(C.Joy_Button_Right) + a$
  1389.    END IF
  1390.   END IF
  1391.   IF C.Joy_Select = -22 THEN _PRINTSTRING (376, 480), "UnDefined" ELSE _PRINTSTRING (376, 480), "Button:" + STR$(C.Joy_Select)
  1392.   IF C.Joy_Start = -22 THEN _PRINTSTRING (444, 436), "UnDefined" ELSE _PRINTSTRING (444, 436), "Button:" + STR$(C.Joy_Start)
  1393.   IF C.Joy_A_Button = -22 THEN _PRINTSTRING (468, 348), "UnDefined" ELSE _PRINTSTRING (468, 348), "Button:" + STR$(C.Joy_A_Button)
  1394.   IF C.Joy_B_Button = -22 THEN _PRINTSTRING (450, 392), "UnDefined" ELSE _PRINTSTRING (450, 392), "Button:" + STR$(C.Joy_B_Button)
  1395.  
  1396. FUNCTION Display_Exit_Controls%% (X%, Y%)
  1397.  IF X% > 640 AND X% < 720 AND Y% > 390 AND Y% < 425 THEN
  1398.   LINE (640, 390)-STEP(80, 35), _RGB32(255, 32, 8), BF
  1399.   _PRINTSTRING (650, 400), "EXIT"
  1400.   Result%% = TRUE
  1401.   LINE (640, 390)-STEP(80, 35), _RGB32(160, 72, 16), BF
  1402.   _PRINTSTRING (650, 400), "EXIT"
  1403.   Result%% = FALSE
  1404.  Display_Exit_Controls = Result%%
  1405.  
  1406. SUB Set_KeyBoard_Control (Master%%, KBD&)
  1407.  SELECT CASE Master%%
  1408.   CASE 1 'Up arrow
  1409.    IF KBD& THEN C.KBCon_Up = KBD&: Master%% = FALSE 'unlock selection with key press
  1410.   CASE 2 'down arrow
  1411.    IF KBD& THEN C.KBCon_Down = KBD&: Master%% = FALSE 'unlock selection with key press
  1412.   CASE 3 'left arrow
  1413.    IF KBD& THEN C.KBCon_Left = KBD&: Master%% = FALSE 'unlock selection with key press
  1414.   CASE 4 'right arrow
  1415.    IF KBD& THEN C.KBCon_Right = KBD&: Master%% = FALSE 'unlock selection with key press
  1416.   CASE 5 'select
  1417.    IF KBD& THEN C.KBCon_Select = KBD&: Master%% = FALSE 'unlock selection with key press
  1418.   CASE 6 'start
  1419.    IF KBD& THEN C.KBCon_Start = KBD&: Master%% = FALSE 'unlock selection with key press
  1420.   CASE 7 'b
  1421.    IF KBD& THEN C.KBCon_B_Button = KBD&: Master%% = FALSE 'unlock selection with key press
  1422.   CASE 8 'a
  1423.    IF KBD& THEN C.KBCon_A_Button = KBD&: Master%% = FALSE 'unlock selection with key press
  1424.  
  1425. SUB Highlite_Select (ID%%)
  1426.  STATIC Factor%%
  1427.  IF ID%% THEN Factor%% = Factor%% + 16 'mouse over highlight color shift
  1428.  IF Factor%% >= 120 THEN Factor%% = 0
  1429.  SELECT CASE ID%% 'highlight which control mouse is over.
  1430.   CASE 1 'up
  1431.    LINE (112, 500)-(292, 504), _RGB32(255 - Factor%%, 127 + Factor%%, 0 + Factor%%), BF
  1432.   CASE 2 'down
  1433.    LINE (144, 412)-(336, 416), _RGB32(0 + Factor%%, 127 - Factor%%, 255 - Factor%%), BF
  1434.   CASE 3 'left
  1435.    LINE (128, 456)-(320, 460), _RGB32(92 + Factor%%, 192 - Factor%%, 0 + Factor%%), BF
  1436.   CASE 4 'right
  1437.    LINE (160, 366)-(340, 370), _RGB32(0 + Factor%%, 64 + Factor%%, 160 - Factor%%), BF
  1438.   CASE 5 'select
  1439.    LINE (344, 500)-(496, 504), _RGB32(255 - Factor%%, 64 + Factor%%, 0 - Factor%%), BF
  1440.   CASE 6 'start
  1441.    LINE (408, 456)-(576, 460), _RGB32(212 - Factor%%, 0 + Factor%%, 16 + Factor%%), BF
  1442.   CASE 7 'B
  1443.    LINE (420, 412)-(576, 416), _RGB32(64 + Factor%%, 92 + Factor%%, 212 - Factor%%), BF
  1444.   CASE 8 'A
  1445.    LINE (436, 368)-(608, 372), _RGB32(80 + Factor%%, 64 + Factor%%, 180 - Factor%%), BF
  1446.  
  1447. SUB Get_JoyPads
  1448.  'load input device details Max:16
  1449.  G.Device_Count = _DEVICES
  1450.  IF G.Device_Count > 16 THEN G.Device_Count = 16 'limit total devices to 16
  1451.  FOR I%% = 1 TO G.Device_Count
  1452.   DeviceData(I%%).Buttons = _LASTBUTTON(I%%) ' number of buttons on the device
  1453.   DeviceData(I%%).Axis_p = _LASTAXIS(I%%) 'number of axis on the device
  1454.  NEXT I%%
  1455.  IF G.Device_Count < 3 THEN G.ControlType = TRUE 'no joypads detected
  1456.  
  1457. SUB Display_BAD_Controller_Option
  1458.  IF C.BAD_Pad THEN
  1459.   LINE (640, 440)-STEP(130, 35), _RGB32(255, 16, 8), BF
  1460.   _PRINTSTRING (650, 450), "TYPE II"
  1461.   Result%% = TRUE
  1462.   LINE (640, 440)-STEP(130, 35), _RGB32(16, 72, 240), BF
  1463.   _PRINTSTRING (660, 450), "TYPE I"
  1464.   Result%% = FALSE
  1465.  
  1466. FUNCTION ControlName$ (id&)
  1467.  Result$ = "#ERROR#"
  1468.  FOR i~%% = 0 TO 133
  1469.   IF KeyCodes(i~%%).Value = id& THEN Result$ = KeyCodes(i~%%).Nam: i~%% = 135
  1470.  NEXT i~%%
  1471.  IF i~%% = 134 THEN Result$ = "Unknown#"
  1472.  ControlName = Result$
  1473.  
  1474. FUNCTION AxisPower%% (CJR%%, CJL%%, CJU%%, CJD%%)
  1475.  CJR%% = _AXIS(C.Joy_Right)
  1476.  CJL%% = _AXIS(C.Joy_Left)
  1477.  CJU%% = _AXIS(C.Joy_Up)
  1478.  CJD%% = _AXIS(C.Joy_Down)
  1479.  IF CJR%% = C.Joy_Right_Val THEN CJL%% = 0: CJR%% = TRUE ELSE CJR%% = 0
  1480.  IF CJD%% = C.Joy_Down_Val THEN CJU%% = 0: CJD%% = TRUE ELSE CJD%% = 0
  1481.  AxisPower = nul%%
  1482.  
  1483. SUB Save_Config
  1484.  IF _FILEEXISTS("DW1CS.CFG") THEN KILL "DW1CS.CFG"
  1485.  CF = FREEFILE
  1486.  OPEN "DW1CS.CFG" FOR BINARY AS #CF
  1487.  Ver~& = CurrentVer
  1488.  PUT #CF, , Ver~&
  1489.  PUT #CF, , C
  1490.  PUT #CF, , G.ControlType
  1491.  ' PUT #CF, , G.TextClick
  1492.  PUT #CF, , G.BGM_vol
  1493.  PUT #CF, , G.SFX_Vol
  1494.  ' PUT #CF, , G.SkipIntro
  1495.  CLOSE #CF
  1496.  
  1497. SUB MFI_Loader (FN$)
  1498.  DIM Size(128) AS LONG, FOffset(128) AS LONG
  1499.  GET #1, , c~%% 'retrieve number of files
  1500.  FOR I~%% = 1 TO c~%%
  1501.   GET #1, , FOffset(I~%%)
  1502.   GET #1, , Size(I~%%)
  1503.   FOffset&(I~%%) = FOffset&(I~%%) + 1
  1504.  NEXT I~%%
  1505.  Layer(7) = LoadGFX(FOffset(1), Size(1)) '_LOADIMAGE("overworldtiles.bmp", 32)
  1506.  Layer(9) = LoadGFX(FOffset(2), Size(2)) '_LOADIMAGE("TitleScreen.bmp", 32)
  1507.  Layer(10) = LoadGFX(FOffset(3), Size(3)) '_LOADIMAGE("Titlefalls.bmp", 32)
  1508.  Layer(11) = LoadGFX(FOffset(4), Size(4)) '_LOADIMAGE("Titlescroll.bmp", 32)
  1509.  Layer(12) = LoadGFX(FOffset(5), Size(5)) '_LOADIMAGE("selectionscreen.bmp", 32)
  1510.  Layer(18) = LoadGFX(FOffset(13), Size(13)) '_LOADIMAGE("gfx\png\controller.png", 32)
  1511.  Layer(17) = LoadGFX(FOffset(14), Size(14)) '_LOADIMAGE("gfx\png\21189.png", 32)
  1512.  
  1513.  SFX(0) = LoadSFX(FOffset(6), Size(6))
  1514.  SFX(1) = LoadSFX(FOffset(7), Size(7))
  1515.  SFX(2) = LoadSFX(FOffset(8), Size(8))
  1516.  SFX(3) = LoadSFX(FOffset(9), Size(9))
  1517.  BGM(0) = LoadSFX(FOffset(10), Size(10))
  1518.  FFX(0) = LoadFFX(FOffset(11), Size(11), 16)
  1519.  LoadData FOffset(12), Size(12)
  1520.  
  1521.  CLOSE #1
  1522.  IF _FILEEXISTS("temp.dat") THEN KILL "temp.dat"
  1523.  
  1524. FUNCTION LoadGFX& (Foff&, Size&)
  1525.  IF _FILEEXISTS("temp.dat") THEN KILL "temp.dat"
  1526.  OPEN "temp.dat" FOR BINARY AS #3
  1527.  dat$ = SPACE$(Size&)
  1528.  GET #1, Foff&, dat$
  1529.  PUT #3, , dat$
  1530.  CLOSE #3
  1531.  LoadGFX& = _LOADIMAGE("temp.dat", 32)
  1532.  
  1533. FUNCTION LoadFFX& (Foff&, Size&, Fize%%)
  1534.  IF _FILEEXISTS("temp.dat") THEN KILL "temp.dat"
  1535.  OPEN "temp.dat" FOR BINARY AS #3
  1536.  dat$ = SPACE$(Size&)
  1537.  GET #1, Foff&, dat$
  1538.  PUT #3, , dat$
  1539.  CLOSE #3
  1540.  LoadFFX& = _LOADFONT("temp.dat", Fize%%, "monospace")
  1541.  
  1542. FUNCTION LoadSFX& (Foff&, Size&)
  1543.  IF _FILEEXISTS("temp.dat") THEN KILL "temp.dat"
  1544.  OPEN "temp.dat" FOR BINARY AS #3
  1545.  dat$ = SPACE$(Size&)
  1546.  GET #1, Foff&, dat$
  1547.  PUT #3, , dat$
  1548.  CLOSE #3
  1549.  LoadSFX& = _SNDOPEN("temp.dat")
  1550.  
  1551. SUB LoadData (Foff&, Size&)
  1552.  IF _FILEEXISTS("temp.dat") THEN KILL "temp.dat"
  1553.  OPEN "temp.dat" FOR BINARY AS #3
  1554.  dat$ = SPACE$(Size&)
  1555.  GET #1, Foff&, dat$
  1556.  PUT #3, , dat$
  1557.  CLOSE #3
  1558.  
  1559.  F1 = FREEFILE
  1560.  OPEN "temp.dat" FOR BINARY AS #F1
  1561.  GET #F1, , Hyrule()
  1562.  GET #F1, , Link
  1563.  GET #F1, , C
  1564.  GET #F1, , G
  1565.  GET #F1, , Offset_X()
  1566.  GET #F1, , Offset_Y()
  1567.  FOR I%% = 1 TO 44
  1568.   GET #F1, , Letter(I%%)
  1569.  NEXT I%%
  1570.  GET #F1, , KeyCodes()
  1571.  GET #F1, , Cave()
  1572.  GET #F1, , Item()
  1573.  GET #F1, , Shop()
  1574.  CLOSE #F1
  1575.  
  1576. SUB Select_Screen
  1577.  ClearLayer Layer(16)
  1578.  IF _FILEEXISTS("Zelda.MSF") THEN 'load saved data
  1579.   OPEN "Zelda.MSF" FOR BINARY AS #1
  1580.   FOR I%% = 1 TO 3 'load all 3 records
  1581.    GET #1, , Nick(I%%)
  1582.    GET #1, , Records(I%%)
  1583.    IF Selection%% = 0 THEN IF RTRIM$(Nick(I%%)) <> "" THEN Selection%% = I%%
  1584.   NEXT I%%
  1585.   CLOSE #1
  1586.  ELSE 'file doesn't exist so make it.
  1587.   Reset_Record 1
  1588.   Reset_Record 2
  1589.   Reset_Record 3
  1590.   Save_Records
  1591.  _PUTIMAGE (140, 72)-STEP(511, 447), Layer(12), Layer(16), (1, 1)-STEP(255, 223) 'background
  1592.  FOR I%% = 1 TO 3
  1593.   IF RTRIM$(Nick(I%%)) <> "" THEN Place_Player_Record I%% - 1, 0
  1594.  NEXT I%%
  1595.  IF Record_Count%% = 0 THEN Selection%% = 4 ELSE Selection%% = 1
  1596.  Selection%% = Find_First_Available(0, 1)
  1597.  
  1598.  DO
  1599.   _PUTIMAGE , Layer(16), Layer(1)
  1600.   SELECT CASE Get_Input%%
  1601.    CASE START_BUTTON
  1602.     SELECT CASE Selection%%
  1603.      CASE 1 TO 3
  1604.       Link = Records(Selection%%)
  1605.       Exitflag%% = TRUE
  1606.      CASE 4
  1607.       DO: LOOP UNTIL Get_Input%% = -1
  1608.       nul%% = Register(nul%%)
  1609.       OPEN "Zelda.MSF" FOR BINARY AS #1 'update records
  1610.       FOR I%% = 1 TO 3
  1611.        PUT #1, , Nick(I%%)
  1612.        PUT #1, , Link
  1613.        IF RTRIM$(Nick(I%%)) <> "" THEN Place_Player_Record I%% - 1, 0
  1614.       NEXT I%%
  1615.       CLOSE #1
  1616.      CASE 5
  1617.       DO: LOOP UNTIL Get_Input%% = -1
  1618.       Elimination_Mode
  1619.       nul%% = Register(nul%%) 'go straight to register mode after elimination
  1620.      CASE 6 'options screen
  1621.       DO: LOOP UNTIL Get_Input%% = -1
  1622.       Options
  1623.     END SELECT
  1624.    CASE SELECT_BUTTON
  1625.     Selection%% = Selection%% + 1
  1626.     IF Selection%% = 7 THEN Selection%% = 1
  1627.     Selection%% = Find_First_Available(0, Selection%%)
  1628.     DO: LOOP UNTIL Get_Input%% = -1
  1629.    CASE ELSE
  1630.     _PRINTSTRING (0, 20), STR$(SELECT_BUTTON), Layer(1)
  1631.  
  1632.   SELECT CASE Selection%%
  1633.    CASE 1
  1634.     _PUTIMAGE (220, 242)-STEP(15, 15), Layer(12), Layer(1), (73, 247)-STEP(7, 7) 'Player 1
  1635.    CASE 2
  1636.     _PUTIMAGE (220, 290)-STEP(15, 15), Layer(12), Layer(1), (73, 247)-STEP(7, 7) 'Player 2
  1637.    CASE 3
  1638.     _PUTIMAGE (220, 338)-STEP(15, 15), Layer(12), Layer(1), (73, 247)-STEP(7, 7) 'Player 3
  1639.    CASE 4
  1640.     _PUTIMAGE (220, 394)-STEP(15, 15), Layer(12), Layer(1), (73, 247)-STEP(7, 7) 'register
  1641.    CASE 5
  1642.     _PUTIMAGE (220, 426)-STEP(15, 15), Layer(12), Layer(1), (73, 247)-STEP(7, 7) 'Elimination
  1643.    CASE 6
  1644.     _PUTIMAGE (220, 458)-STEP(15, 15), Layer(12), Layer(1), (73, 247)-STEP(7, 7) 'Options
  1645.   _PRINTSTRING (0, 0), STR$(Selection%%), Layer(1)
  1646.   _PUTIMAGE , Layer(1), Layer(0)
  1647.   _LIMIT 60
  1648.   IF INKEY$ = CHR$(27) THEN Exitflag%% = TRUE
  1649.  LOOP UNTIL Exitflag%%
  1650.  
  1651. FUNCTION Register%% (Records%%)
  1652.  DIM Names(2, 8) AS STRING * 1
  1653.  Result%% = Records%%
  1654.  Tmp& = _COPYIMAGE(Layer(16))
  1655.  ClearLayer Layer(16)
  1656.  ClearLayer Layer(1)
  1657.  _PUTIMAGE (140, 72)-STEP(511, 447), Layer(12), Layer(16), (258, 1)-STEP(255, 223) 'background
  1658.  FOR i%% = 1 TO 3
  1659.   IF RTRIM$(Nick(i%%)) <> "" THEN Place_Player_Record i%% - 1, 1
  1660.  NEXT i%%
  1661.  IF i%% > 0 THEN Selection%% = i%% ELSE Selection%% = 1
  1662.  FOR i%% = 1 TO 3
  1663.   IF RTRIM$(Nick(i%%)) = "" THEN _PUTIMAGE (300, 154 + 48 * (i%% - 1))-STEP(31, 31), Layer(12), Layer(16), (1, 230)-STEP(15, 15) 'Player 1 Link (green)
  1664.  NEXT i%%
  1665.  _CLEARCOLOR _RGB32(0), Layer(16)
  1666.  _PUTIMAGE , Layer(16), Layer(1)
  1667.  _DEST Layer(1)
  1668.  Current_Letter%% = 1
  1669.  Selection%% = Find_First_Available(1, 1)
  1670.  DO
  1671.  
  1672.   SELECT CASE Get_Input%%
  1673.    CASE Up
  1674.     _PUTIMAGE (236 + 32 * Lx%, 328 + 32 * Ly%)-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'Current selected Letter
  1675.     Ly% = Ly% - 1
  1676.     IF Ly% = -1 THEN Ly% = 3
  1677.     Current_Letter%% = Current_Letter%% - 11
  1678.     IF Current_Letter%% < 0 THEN Current_Letter%% = 44 - ABS(Current_Letter%%)
  1679.    CASE Down
  1680.     _PUTIMAGE (236 + 32 * Lx%, 328 + 32 * Ly%)-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'Current selected Letter
  1681.     Ly% = Ly% + 1
  1682.     IF Ly% = 4 THEN Ly% = 0
  1683.     Current_Letter%% = Current_Letter%% + 11
  1684.     IF Current_Letter%% > 44 THEN Current_Letter%% = Current_Letter%% - 44
  1685.    CASE Left
  1686.     _PUTIMAGE (236 + 32 * Lx%, 328 + 32 * Ly%)-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'Current selected Letter
  1687.     Lx% = Lx% - 1
  1688.     IF Lx% = -1 THEN Lx% = 10: Ly% = Ly% - 1: IF Ly% = -1 THEN Ly% = 3
  1689.     Current_Letter%% = Current_Letter%% - 1
  1690.     IF Current_Letter%% = 0 THEN Current_Letter%% = 44
  1691.    CASE Right
  1692.     _PUTIMAGE (236 + 32 * Lx%, 328 + 32 * Ly%)-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'Current selected Letter
  1693.     Lx% = Lx% + 1
  1694.     IF Lx% = 11 THEN Lx% = 0: Ly% = Ly% + 1: IF Ly% = 4 THEN Ly% = 0
  1695.     Current_Letter%% = Current_Letter%% + 1
  1696.     IF Current_Letter%% = 45 THEN Current_Letter%% = 1
  1697.    CASE BUTTON_A OR BUTTON_B
  1698.     IF Selection%% <> 4 THEN 'only allow buttons if valid name entry selection
  1699.      _PUTIMAGE (364 + 16 * Length%%, 152 + 48 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'Current Nick Letter
  1700.      Names(Selection%% - 1, Length%%) = Letter(Current_Letter%%)
  1701.      Length%% = Length%% + 1
  1702.      IF Length%% = 8 THEN Length%% = 0
  1703.     END IF
  1704.    CASE START_BUTTON
  1705.     SELECT CASE Selection%%
  1706.      CASE 4 'end
  1707.       FOR j%% = 0 TO 2
  1708.        a$ = ""
  1709.        IF RTRIM$(Nick(j%% + 1)) = "" THEN
  1710.         FOR i%% = 0 TO 7
  1711.          IF ASC(Names(j%%, i%%)) > 31 THEN a$ = a$ + Names(j%%, i%%)
  1712.         NEXT
  1713.         Nick(j%% + 1) = a$
  1714.        END IF
  1715.       NEXT
  1716.       ExitFlag%% = TRUE
  1717.     END SELECT
  1718.    CASE SELECT_BUTTON 'Change to different name or end registration
  1719.     _PUTIMAGE (364 + 16 * Length%%, 152 + 48 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'Current Nick Letter
  1720.     _PUTIMAGE (274, 152 + 48 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'heart selection
  1721.     Selection%% = Selection%% + 1: Length%% = 0 'reset the name position when changing.
  1722.     IF Selection%% = 5 THEN Selection%% = 1
  1723.     _PRINTSTRING (0, 0), STR$(Selection%%), Layer(1)
  1724.     Selection%% = Find_First_Available(1, Selection%%)
  1725.    CASE ELSE
  1726.     LINE (0, 0)-STEP(160, 40), _RGB32(0), BF
  1727.     _PRINTSTRING (0, 20), STR$(Selection%%), Layer(1)
  1728.   DO: LOOP UNTIL Get_Input%% = -1
  1729.  
  1730.   _PUTIMAGE (274, 152 + 48 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (73, 247)-STEP(7, 7) 'heart selection
  1731.   IF Selection%% <> 4 THEN
  1732.    FOR j%% = 0 TO 8
  1733.     IF Names(Selection%% - 1, j%%) > CHR$(31) THEN _PRINTSTRING (364 + 16 * j%%, 152 + 48 * (Selection%% - 1)), Names(Selection%% - 1, j%%), Layer(1)
  1734.    NEXT j%%
  1735.   END IF
  1736.   _PUTIMAGE , Layer(1), Layer(0)
  1737.   IF blink%% AND Selection%% <> 4 THEN
  1738.    _PUTIMAGE (364 + 16 * Length%%, 152 + 48 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (61, 230)-STEP(7, 7) 'Current Nick Letter
  1739.    _PUTIMAGE (236 + 32 * Lx%, 328 + 32 * Ly%)-STEP(15, 15), Layer(12), Layer(1), (61, 230)-STEP(7, 7) 'Current selected Letter
  1740.   ELSE
  1741.    _PUTIMAGE (364 + 16 * Length%%, 152 + 48 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'Current Nick Letter
  1742.    _PUTIMAGE (236 + 32 * Lx%, 328 + 32 * Ly%)-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'Current selected Letter
  1743.   END IF
  1744.   _PUTIMAGE , Layer(16), Layer(1)
  1745.   b%% = b%% + 1
  1746.   IF b%% = 8 THEN blink%% = NOT blink%%: b%% = 0
  1747.   _LIMIT 60
  1748.   IF INKEY$ = CHR$(27) THEN ExitFlag%% = TRUE
  1749.  LOOP UNTIL ExitFlag%%
  1750.  ClearLayer Layer(16)
  1751.  _PUTIMAGE , Tmp&, Layer(16)
  1752.  _FREEIMAGE Tmp&
  1753.  Save_Records
  1754.  FOR i%% = 1 TO 3
  1755.   IF RTRIM$(Nick(i%%)) <> "" THEN Place_Player_Record i%% - 1, 0
  1756.  NEXT i%%
  1757.  Register = Result%%
  1758.  
  1759. SUB Elimination_Mode
  1760.  Tmp& = _COPYIMAGE(Layer(16))
  1761.  ClearLayer Layer(16)
  1762.  ClearLayer Layer(1)
  1763.  _PUTIMAGE (140, 72)-STEP(511, 447), Layer(12), Layer(16), (515, 1)-STEP(255, 223) 'background
  1764.  FOR i%% = 1 TO 3
  1765.   IF RTRIM$(Nick(i%%)) <> "" THEN Place_Player_Record i%% - 1, 1
  1766.  NEXT i%%
  1767.  _PUTIMAGE , Layer(16), Layer(0)
  1768.  IF i%% > 0 THEN Selection%% = i%% ELSE Selection%% = 1
  1769.  FOR i%% = 1 TO 3 'remove the erased game from the background screens
  1770.   IF RTRIM$(Nick(i%%)) = "" THEN _PUTIMAGE (300, 154 + 48 * (i%% - 1))-STEP(31, 31), Layer(12), Layer(16), (1, 230)-STEP(15, 15) 'Player 1 Link (green)
  1771.  NEXT i%%
  1772.  _CLEARCOLOR _RGB32(0), Layer(16)
  1773.  _PUTIMAGE , Layer(16), Layer(1)
  1774.  _DEST Layer(1)
  1775.  Current_Letter%% = 1
  1776.  Selection%% = 1
  1777.  DO
  1778.   _PUTIMAGE , Layer(16), Layer(1)
  1779.   SELECT CASE Get_Input%%
  1780.    CASE START_BUTTON
  1781.     SELECT CASE Selection%%
  1782.      CASE 1 TO 3
  1783.       Nick(Selection%%) = ""
  1784.       Reset_Record Selection%%
  1785.       _PUTIMAGE (364, 152 + 48 * (Selection%% - 1))-STEP(159, 31), Layer(12), Layer(16), (86, 240)-STEP(7, 7) 'black out
  1786.       _PUTIMAGE (236, 232 + 48 * (Selection%% - 1))-STEP(159, 33), Layer(12), Tmp&, (86, 240)-STEP(7, 7) 'black out name
  1787.       _PUTIMAGE (428, 232 + 48 * (Selection%% - 1))-STEP(127, 31), Layer(12), Tmp&, (86, 240)-STEP(7, 7) 'black out hearts
  1788.      CASE 4
  1789.       ExitFlag%% = TRUE
  1790.     END SELECT
  1791.    CASE SELECT_BUTTON
  1792.     _PUTIMAGE (274, 152 + 48 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'heart selection black out
  1793.     Selection%% = Selection%% + 1
  1794.     IF Selection%% = 5 THEN Selection%% = 1
  1795.    CASE ELSE
  1796.     _PRINTSTRING (0, 20), STR$(SELECT_BUTTON), Layer(1)
  1797.   DO: LOOP UNTIL Get_Input%% = -1
  1798.   _PUTIMAGE (274, 152 + 48 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (73, 267)-STEP(7, 7) 'heart selection
  1799.   _PUTIMAGE , Layer(1), Layer(0)
  1800.   _LIMIT 60
  1801.   IF INKEY$ = CHR$(27) THEN ExitFlag%% = TRUE
  1802.  LOOP UNTIL ExitFlag%%
  1803.  ClearLayer Layer(16)
  1804.  _PUTIMAGE , Tmp&, Layer(16)
  1805.  _FREEIMAGE Tmp&
  1806.  
  1807. SUB Reset_Record (Which%%)
  1808.  Records(Which%%) = Reset_Link
  1809.  
  1810. SUB Save_Records
  1811.  OPEN "Zelda.MSF" FOR BINARY AS #1
  1812.  FOR I%% = 1 TO 3
  1813.   PUT #1, , Nick(I%%)
  1814.   PUT #1, , Records(I%%)
  1815.  NEXT I%%
  1816.  CLOSE #1
  1817.  
  1818. SUB Options
  1819.  STATIC BGMV AS SINGLE, SFXV AS SINGLE, MSTR AS SINGLE
  1820.  Selection%% = 1
  1821.  MSTR = G.MSTR_Vol / 100
  1822.  BGMV = G.BGM_vol / 100
  1823.  SFXV = G.SFX_Vol / 100
  1824.  
  1825.  Tmp& = _COPYIMAGE(Layer(16))
  1826.  ClearLayer Layer(16)
  1827.  ClearLayer Layer(1)
  1828.  _PUTIMAGE (140, 72)-STEP(511, 447), Layer(12), Layer(16), (772, 1)-STEP(255, 223) 'background
  1829.  _PRINTSTRING (128, 144), "Master Volume:", Layer(16)
  1830.  _PRINTSTRING (128, 176), "Music  Volume:", Layer(16)
  1831.  _PRINTSTRING (128, 208), "Sounds Volume:", Layer(16)
  1832.  _PRINTSTRING (128, 256), "Scale Factor :", Layer(16)
  1833.  _PRINTSTRING (128, 352), "Controler Setup", Layer(16)
  1834.  _PRINTSTRING (128, 384), "OPTIONS EXIT", Layer(16)
  1835.  _DEST Layer(1)
  1836.  DO
  1837.   _PUTIMAGE , Layer(16), Layer(1)
  1838.  
  1839.   SELECT CASE Get_Input%%
  1840.    CASE START_BUTTON
  1841.     SELECT CASE Selection%%
  1842.      CASE 1
  1843.       DO: LOOP UNTIL Get_Input%% = -1
  1844.       Custom_Controls
  1845.      CASE 2
  1846.       ExitFlag%% = TRUE
  1847.     END SELECT
  1848.    CASE SELECT_BUTTON
  1849.     _PUTIMAGE (112, 352 + 32 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (86, 240)-STEP(7, 7) 'heart selection black out
  1850.     Selection%% = Selection%% + 1
  1851.     IF Selection%% = 3 THEN Selection%% = 1
  1852.    CASE ELSE
  1853.   DO: LOOP UNTIL Get_Input%% = -1
  1854.  
  1855.    IF Locked%% = 0 THEN 'player just clicked mouse
  1856.     X% = _MOUSEX: y% = _MOUSEY
  1857.     IF X% > 359 AND X% < 617 THEN 'mouse is in the volume slider area
  1858.      IF y% > 143 AND y% < 160 THEN Locked%% = 1 'master volume slider selected
  1859.      IF y% > 175 AND y% < 192 THEN Locked%% = 2 'Music volume slider selected
  1860.      IF y% > 207 AND y% < 224 THEN Locked%% = 3 'Sounds volume slider selected
  1861.      IF y% > 255 AND y% < 272 THEN Locked%% = 4 'Scale slider selected
  1862.     END IF
  1863.    ELSE 'player has a selected volume slider
  1864.     X% = _MOUSEX
  1865.     IF X% > 359 AND X% < 617 THEN 'mouse is in the volume slider area
  1866.      NewVol! = (X% - 359) / 256
  1867.      SELECT CASE Locked%%
  1868.       CASE 1 'set new master volume
  1869.        MSTR = NewVol!
  1870.       CASE 2 'set new Music volume
  1871.        BGMV = NewVol!
  1872.       CASE 3 'set new sounds volume
  1873.        SFXV = NewVol!
  1874.       CASE 4 'set new scale
  1875.        SELECT CASE NewVol!
  1876.         CASE 0 TO .33 '1x scale
  1877.          G.Scale_Factor = 1
  1878.         CASE .34 TO .66 '2x scale
  1879.          G.Scale_Factor = 2
  1880.         CASE .67 TO 1 '3x scale
  1881.          G.Scale_Factor = 3
  1882.        END SELECT
  1883.      END SELECT
  1884.     END IF
  1885.    END IF
  1886.   ELSEIF Locked%% THEN 'player released the mouse button
  1887.    Locked%% = FALSE
  1888.   END IF
  1889.   Update_Volume_Bars MSTR, BGMV, SFXV
  1890.   _PUTIMAGE (112, 352 + 32 * (Selection%% - 1))-STEP(15, 15), Layer(12), Layer(1), (73, 247)-STEP(7, 7) 'heart selection
  1891.   _PUTIMAGE , Layer(1), Layer(0)
  1892.   _LIMIT 60
  1893.   IF INKEY$ = CHR$(27) THEN ExitFlag%% = TRUE
  1894.  LOOP UNTIL ExitFlag%%
  1895.  '------Make the changes final-----
  1896.  G.MSTR_Vol = INT(MSTR * 100)
  1897.  G.BGM_vol = INT(BGMV * MSTR * 100)
  1898.  G.SFX_Vol = INT(SFXV * MSTR * 100)
  1899.  G.Scale_X = 16 * G.Scale_Factor - 1
  1900.  G.Scale_Y = 16 * G.Scale_Factor - 1
  1901.  Link.Screen_X = Offset_X(G.Scale_Factor) + (16 * G.Scale_Factor * 7) + 8 * G.Scale_Factor '392
  1902.  Link.Screen_Y = Offset_Y(G.Scale_Factor) + (16 * G.Scale_Factor * 5) '292
  1903.  '---------------------------------
  1904.  ClearLayer Layer(16)
  1905.  _PUTIMAGE , Tmp&, Layer(16)
  1906.  _FREEIMAGE Tmp&
  1907.  
  1908. SUB Update_Volume_Bars (M!, B!, S!)
  1909.  LINE (360, 144)-STEP(16 * 16, 16), _RGB32(0 + INT(255 * M!), 255 - INT(255 * M!), 0), BF
  1910.  LINE (360, 176)-STEP(16 * 16, 16), _RGB32(0 + INT(255 * (B! * M!)), 255 - INT(255 * (B! * M!)), 0), BF
  1911.  LINE (360, 208)-STEP(16 * 16, 16), _RGB32(0 + INT(255 * (S! * M!)), 255 - INT(255 * (S! * M!)), 0), BF
  1912.  LINE (360, 256)-STEP(16 * 16, 16), _RGB32(0 + 128 * (G.Scale_Factor - 1), 255 - 128 * (G.Scale_Factor - 1), 0), BF
  1913.  LINE (360 + (256 * M!), 144)-STEP(1, 20), _RGB32(224, 224, 224), B
  1914.  LINE (360 + (256 * B!), 176)-STEP(1, 20), _RGB32(224, 224, 224), B
  1915.  LINE (360 + (256 * S!), 208)-STEP(1, 20), _RGB32(224, 224, 224), B
  1916.  LINE (360 + 128 * (G.Scale_Factor - 1), 256)-STEP(1, 20), _RGB32(224, 224, 224), B
  1917.  _PRINTSTRING (640, 144), LTRIM$(STR$(INT(M! * 100))) + "%"
  1918.  _PRINTSTRING (640, 176), LTRIM$(STR$(INT((B! * M!) * 100))) + "%"
  1919.  _PRINTSTRING (640, 208), LTRIM$(STR$(INT((S! * M!) * 100))) + "%"
  1920.  _PRINTSTRING (640, 256), LTRIM$(STR$(G.Scale_Factor)) + "x"
  1921.  a$ = LTRIM$(STR$(256 * G.Scale_Factor)) + "x" + LTRIM$(STR$(224 * G.Scale_Factor))
  1922.  _PRINTSTRING (440, 276), a$
  1923.  
* Zelda.MFI (Filesize: 2.09 MB, Downloads: 193)
Granted after becoming radioactive I only have a half-life!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
Re: WIP: game input mapping for gamepad, keyboard, v0.90
« Reply #2 on: January 06, 2022, 10:39:03 pm »
Slight update, where I fixed the keyboard input and the joysticks now all have continuous movement.
Now the only problem is STOPPING the continuous movement when you want it stopped.
Particularly, if you hold down the fire button, it shouldn't work again until you release it first.
I have variables to compare, and the button should only register if previous value was FALSE (not pressed):
Code: QB64: [Select]
  1. if m_arrPlayer(iPlayer).lastButton1 <> TRUE then
but for some reason, no workie!

Apart from that, it's all working now. Here is the latest:
Code: QB64: [Select]
  1. ' ################################################################################################################################################################
  2. ' #TOP
  3.  
  4. ' Game Input Mapping Test
  5. ' Version 0.90 by madscijr
  6.  
  7. ' BASED ON CODE BY SMcNeill FROM:
  8. '     Simple Joystick Detection and Interaction  (Read 316 times)
  9. '     https://www.qb64.org/forum/index.php?topic=2160.msg129051#msg129051
  10. '     https://qb64forum.alephc.xyz/index.php?topic=2160.msg129083#msg129083
  11. ' and others (sources cited throughout).
  12.  
  13. ' ################################################################################################################################################################
  14. ' #CONSTANTS = GLOBAL CONSTANTS
  15.  
  16. ' boolean constants:
  17. Const FALSE = 0
  18. Const TRUE = Not FALSE
  19.  
  20. ' BEGIN GAME CONTROLLER MAPPING CONSTANTS
  21. Const cInputNone = 0
  22. Const cInputKey = 1
  23. Const cInputButton = 2
  24. Const cInputAxis = 3
  25.  
  26. Const cMaxButtons = 12
  27. Const cMaxAxis = 8
  28. Const cMaxControllers = 8
  29. Const cMaxPlayers = 8
  30.  
  31. ' Use as index for array of ControlInputType
  32. Const cInputUp = 1
  33. Const cInputDown = 2
  34. Const cInputLeft = 3
  35. Const cInputRight = 4
  36. Const cInputButton1 = 5
  37. Const cInputButton2 = 6
  38. Const cInputButton3 = 7
  39. Const cInputButton4 = 8
  40.  
  41. Const c_iKeyDown_F10 = 17408
  42. Const c_iKeyHit_AltLeft = -30764
  43. Const c_iKeyHit_AltRight = -30765
  44. ' END GAME CONTROLLER MAPPING CONSTANTS
  45.  
  46. ' ################################################################################################################################################################
  47. ' #UDT #TYPES = USER DEFINED TYPES
  48.  
  49. ' UDT TO HOLD THE INFO FOR A PLAYER
  50. Type PlayerType
  51.     x As Integer ' player x position
  52.     y As Integer ' player y position
  53.     c As Integer ' character to display on screen
  54.     xOld As Integer
  55.     yOld As Integer
  56.  
  57.     ' control buffer + previous move
  58.     moveX As Integer
  59.     lastMoveX As Integer
  60.     moveY As Integer
  61.     lastMoveY As Integer
  62.     button1 As Integer
  63.     lastButton1 As Integer
  64.     button2 As Integer
  65.     lastButton2 As Integer
  66.     button3 As Integer
  67.     lastButton3 As Integer
  68.     button4 As Integer
  69.     lastButton4 As Integer
  70. End Type ' PlayerType
  71.  
  72. ' UDT TO HOLD THE INFO FOR A GAME CONTROLLER
  73. Type ControllerType
  74.     buttonCount As Integer
  75.     axisCount As Integer
  76. End Type ' ControllerType
  77.  
  78. ' UDT TO HOLD THE INFO FOR A GAME CONTROLLER
  79. Type ControlInputType
  80.     device As Integer
  81.     typ As Integer ' cInputKey, cInputButton, cInputAxis
  82.     code As Integer
  83.     value As Integer
  84. End Type ' ControlInputType
  85.  
  86. ' ################################################################################################################################################################
  87. ' #VARS = GLOBAL VARIABLES
  88.  
  89. ' ENABLE / DISABLE DEBUG CONSOLE
  90. Dim Shared m_bTesting As Integer: m_bTesting = TRUE
  91.  
  92. ' BASIC PROGRAM METADATA
  93. Dim Shared m_ProgramPath$: m_ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
  94. Dim Shared m_ProgramName$: m_ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
  95.  
  96. ' GAME CONTROLLER MAPPING
  97. Dim Shared m_ControlMapFileName$: m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  98. ReDim Shared m_arrControlMap(1 To 8, 1 To 8) As ControlInputType ' holds control mapping for each player (player #, direction)
  99. ReDim Shared m_arrController(1 To 8) As ControllerType ' holds info for each game controller
  100. ReDim Shared m_arrButtonCode(1 To 99) As Integer ' Long
  101. ReDim Shared m_arrButtonKey(1 To 99) As String
  102. ReDim Shared m_arrButtonKeyDesc(0 To 512) As String
  103.  
  104. ' VARIABLES FOR GRAPHIC PRINTING ROUTINES
  105. Dim Shared m_NumColumns As Integer: m_NumColumns = 1
  106. Dim Shared m_PrintRow As Integer: m_PrintRow = 0
  107. Dim Shared m_PrintCol As Integer: m_PrintCol = 0
  108. Dim Shared m_StartRow As Integer: m_StartRow = 0
  109. Dim Shared m_EndRow As Integer: m_EndRow = 0
  110. Dim Shared m_StartCol As Integer: m_StartCol = 0
  111. Dim Shared m_EndCol As Integer: m_EndCol = 0
  112.  
  113. ' DEMO GAME / TESTING
  114. ReDim Shared m_arrPlayer(1 To 8) As PlayerType ' holds info for each player
  115.  
  116. ' =============================================================================
  117. ' LOCAL VARIABLES
  118. Dim in$
  119.  
  120. ' ****************************************************************************************************************************************************************
  121. ' ACTIVATE DEBUGGING WINDOW
  122. If m_bTesting = TRUE Then
  123.     $Console
  124.     _Delay 4
  125.     _Console On
  126.     _Echo "Started " + m_ProgramName$
  127.     _Echo "Debugging on..."
  128. ' ****************************************************************************************************************************************************************
  129.  
  130. ' =============================================================================
  131. ' START THE MAIN ROUTINE
  132. main
  133.  
  134. ' =============================================================================
  135. ' FINISH
  136. Print m_ProgramName$ + " finished."
  137. Input "Press <ENTER> to continue", in$
  138.  
  139. ' ****************************************************************************************************************************************************************
  140. ' DEACTIVATE DEBUGGING WINDOW
  141. If m_bTesting = TRUE Then
  142. ' ****************************************************************************************************************************************************************
  143.  
  144. System ' return control to the operating system
  145.  
  146. ' /////////////////////////////////////////////////////////////////////////////
  147.  
  148. Sub main
  149.     Dim RoutineName As String: RoutineName = "main"
  150.     Dim in$
  151.     Dim result$: result$ = ""
  152.  
  153.     ' SET UP SCREEN
  154.     Screen _NewImage(1280, 1024, 32): _ScreenMove 0, 0
  155.  
  156.     Do
  157.         If Len(result$) = 0 Then
  158.             Cls
  159.         Else
  160.             Print
  161.         End If
  162.  
  163.         Print m_ProgramName$
  164.         Print
  165.         Print "Game Input Mapping Test"
  166.         Print "v0.90, by Softintheheadware (Jan, 2022)"
  167.         Print
  168.  
  169.         Print "1. Basic controller test"
  170.         Print "2. Load controller mapping"
  171.         Print "3. View controller mapping"
  172.         Print "4. Edit  controller mapping for 1 or more players"
  173.         Print "   Note: not tested yet"
  174.         Print "5. Reset controller mapping for 1 or more players"
  175.         Print "6. Map controllers for 1 or more players"
  176.         Print "7. Test controller mappings to move around screen"
  177.         Print "   Issue #1: Keyboard input not working yet"
  178.         Print "   Issue #2: Digital joysticks don't allow continuous movement"
  179.         Print "   Issue #3: Need analog joystick sensitivity adjustment option"
  180.         Print "8. Save controller mappings"
  181.         Print
  182.         Print "What to do? ('q' to exit)"
  183.  
  184.         Input in$: in$ = LCase$(Left$(in$, 1))
  185.  
  186.         If in$ = "1" Then
  187.             result$ = TestJoysticks$
  188.         ElseIf in$ = "2" Then
  189.             result$ = LoadMappings$
  190.             If Len(result$) = 0 Then result$ = "Loaded mappings."
  191.         ElseIf in$ = "3" Then
  192.             result$ = ViewMappings$
  193.         ElseIf in$ = "4" Then
  194.             result$ = EditMappings$
  195.         ElseIf in$ = "5" Then
  196.             result$ = ResetMapping$
  197.         ElseIf in$ = "6" Then
  198.             result$ = MapInput$
  199.         ElseIf in$ = "7" Then
  200.             result$ = TestMappings$
  201.         ElseIf in$ = "8" Then
  202.             result$ = SaveMappings$
  203.         End If
  204.  
  205.         If Len(result$) > 0 Then
  206.             Print result$
  207.         End If
  208.  
  209.     Loop Until in$ = "q"
  210.  
  211.     ' RETURN TO TEXT SCREEN
  212.     Screen 0
  213.  
  214. End Sub ' main
  215.  
  216. ' /////////////////////////////////////////////////////////////////////////////
  217. ' TODO: get keyboard input working
  218. ' TODO: get continuous movement working for digital joysticks
  219. ' TODO: adjust analog joystick sensitivity
  220.  
  221. Function TestMappings$
  222.     Dim iDeviceCount As Integer
  223.     Dim iDevice As Integer
  224.     Dim iNumControllers As Integer
  225.     Dim iController As Integer
  226.     Dim iValue As Integer
  227.     Dim iWhichInput As Integer
  228.  
  229.     Dim arrButton(32, 16) As Integer ' number of buttons on the joystick
  230.     Dim arrButtonNew(32, 16) As Integer ' tracks when to initialize values
  231.     Dim arrAxis(32, 16) As Double ' number of axis on the joystick
  232.     Dim arrAxisNew(32, 16) As Integer ' tracks when to initialize values
  233.  
  234.     Dim iCols As Integer
  235.     Dim iRows As Integer
  236.  
  237.     Dim iPlayer As Integer
  238.     Dim iNextY As Integer
  239.     Dim iNextX As Integer
  240.     Dim iNextC As Integer
  241.  
  242.     Dim iMinX As Integer
  243.     Dim iMaxX As Integer
  244.     Dim iMinY As Integer
  245.     Dim iMaxY As Integer
  246.  
  247.     Dim sError As String: sError = ""
  248.     Dim bHaveInput As Integer
  249.     Dim bFinished As Integer
  250.     Dim bFoundWho As Integer
  251.  
  252.     Dim in$
  253.  
  254.     ' INITIALIZE
  255.     Cls
  256.     iCols = _Width(0) \ _FontWidth
  257.     iRows = _Height(0) \ _FontHeight
  258.     iMinX = 0: iMaxX = iCols
  259.     iMinY = 0: iMaxY = iRows
  260.  
  261.     ' INITIALIZE PLAYER COORDINATES AND SCREEN CHARACTERS
  262.     iNextY = 1
  263.     iNextX = -3
  264.     iNextC = 64
  265.     For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  266.         iNextX = iNextX + 4
  267.         If iNextX >= iMaxX Then
  268.             iNextX = iMinX
  269.             iNextY = iNextY + 4
  270.             If iNextY > iMaxY Then
  271.                 iNextY = iMinY
  272.             End If
  273.         End If
  274.         iNextC = iNextC + 1
  275.         m_arrPlayer(iPlayer).x = iNextX
  276.         m_arrPlayer(iPlayer).y = iNextY
  277.         m_arrPlayer(iPlayer).c = iNextC
  278.         m_arrPlayer(iPlayer).xOld = iNextX
  279.         m_arrPlayer(iPlayer).yOld = iNextY
  280.  
  281.         m_arrPlayer(iPlayer).moveX = 0
  282.         m_arrPlayer(iPlayer).lastMoveX = 0
  283.         m_arrPlayer(iPlayer).moveY = 0
  284.         m_arrPlayer(iPlayer).lastMoveY = 0
  285.         m_arrPlayer(iPlayer).button1 = 0
  286.         m_arrPlayer(iPlayer).lastButton1 = 0
  287.         m_arrPlayer(iPlayer).button2 = 0
  288.         m_arrPlayer(iPlayer).lastButton2 = 0
  289.         m_arrPlayer(iPlayer).button3 = 0
  290.         m_arrPlayer(iPlayer).lastButton3 = 0
  291.         m_arrPlayer(iPlayer).button4 = 0
  292.         m_arrPlayer(iPlayer).lastButton4 = 0
  293.     Next iPlayer
  294.  
  295.     ' COUNT # OF JOYSTICKS
  296.     ' TODO: find out the right way to count joysticks
  297.     If Len(sError) = 0 Then
  298.         ' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
  299.         iDeviceCount = _Devices ' Find the number of devices on someone's system
  300.  
  301.         If iDeviceCount > 2 Then
  302.             ' LIMIT # OF DEVICES, IF THERE IS A LIMIT DEFINED
  303.             iNumControllers = iDeviceCount - 2
  304.             If cMaxControllers > 0 Then
  305.                 If iNumControllers > cMaxControllers Then
  306.                     iNumControllers = cMaxControllers
  307.                 End If
  308.             End If
  309.         Else
  310.             ' ONLY 2 FOUND (KEYBOARD, MOUSE)
  311.             'sError = "No game controllers found."
  312.             iNumControllers = 0
  313.         End If
  314.     End If
  315.  
  316.     ' INITIALIZE CONTROLLER DATA
  317.     If Len(sError) = 0 Then
  318.         For iController = 1 To iNumControllers
  319.             m_arrController(iController).buttonCount = cMaxButtons
  320.             m_arrController(iController).axisCount = cMaxAxis
  321.             For iLoop = 1 To cMaxButtons
  322.                 arrButtonNew(iController, iLoop) = TRUE
  323.             Next iLoop
  324.             For iLoop = 1 To cMaxAxis
  325.                 arrAxisNew(iController, iLoop) = TRUE
  326.             Next iLoop
  327.         Next iController
  328.     End If
  329.  
  330.     ' INITIALIZE CONTROLLER INPUT
  331.     If Len(sError) = 0 Then
  332.         _KeyClear: _Delay 1
  333.         For iController = 1 To iNumControllers
  334.             iDevice = iController + 2
  335.             While _DeviceInput(iDevice) ' clear and update the device buffer
  336.                 For iLoop = 1 To _LastButton(iDevice)
  337.                     If (iLoop > cMaxButtons) Then Exit For
  338.                     m_arrController(iController).buttonCount = iLoop
  339.                     arrButton(iController, iLoop) = FALSE
  340.                 Next iLoop
  341.                 For iLoop = 1 To _LastAxis(iDevice) ' this loop checks all my axis
  342.                     If (iLoop > cMaxAxis) Then Exit For
  343.                     m_arrController(iController).axisCount = iLoop
  344.                     arrAxis(iController, iLoop) = 0
  345.                 Next iLoop
  346.             Wend ' clear and update the device buffer
  347.         Next iController
  348.     End If
  349.  
  350.     ' GET INPUT AND MOVE PLAYERS AROUND ON SCREEN
  351.     _KeyClear: _Delay 1
  352.     bFinished = FALSE
  353.     Do
  354.         ' Clear control buffer for players
  355.         For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  356.             m_arrPlayer(iPlayer).lastMoveX = m_arrPlayer(iPlayer).moveX
  357.             m_arrPlayer(iPlayer).moveX = 0
  358.             m_arrPlayer(iPlayer).lastMoveY = m_arrPlayer(iPlayer).moveY
  359.             m_arrPlayer(iPlayer).moveY = 0
  360.             m_arrPlayer(iPlayer).lastButton1 = m_arrPlayer(iPlayer).button1
  361.             m_arrPlayer(iPlayer).button1 = 0
  362.             m_arrPlayer(iPlayer).lastButton2 = m_arrPlayer(iPlayer).button2
  363.             m_arrPlayer(iPlayer).button2 = 0
  364.             m_arrPlayer(iPlayer).lastButton3 = m_arrPlayer(iPlayer).button3
  365.             m_arrPlayer(iPlayer).button3 = 0
  366.             m_arrPlayer(iPlayer).lastButton4 = m_arrPlayer(iPlayer).button4
  367.             m_arrPlayer(iPlayer).button4 = 0
  368.         Next iPlayer
  369.  
  370.         ' -----------------------------------------------------------------------------
  371.         ' BEGIN CHECK FOR CONTROLLER INPUT
  372.         If iNumControllers > 0 Then
  373.             For iController = 1 To iNumControllers
  374.                 iDevice = iController + 2
  375.  
  376.                 ' Check all devices
  377.                 While _DeviceInput(iDevice)
  378.                 Wend ' clear and update the device buffer
  379.  
  380.                 ' Check each button
  381.                 For iLoop = 1 To _LastButton(iDevice)
  382.                     If (iLoop > cMaxButtons) Then Exit For
  383.  
  384.                     ' update button array to indicate if a button is up or down currently.
  385.                     'if TRUE=TRUE then
  386.                     If _ButtonChange(iLoop) Then
  387.                         iValue = _Button(iLoop)
  388.                         If iValue <> arrButton(iController, iLoop) Then
  389.                             ' *****************************************************************************
  390.                             ' PRESSED BUTTON
  391.  
  392.                             ' BEGIN find who this is mapped for
  393.                             bFoundWho = FALSE
  394.                             For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  395.                                 For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  396.                                     If m_arrControlMap(iPlayer, iWhichInput).device = iDevice Then
  397.                                         If m_arrControlMap(iPlayer, iWhichInput).typ = cInputButton Then
  398.                                             If m_arrControlMap(iPlayer, iWhichInput).code = iLoop Then
  399.                                                 'if m_arrControlMap(iPlayer, iWhichInput).value = iValue then
  400.                                                 bFoundWho = TRUE
  401.                                                 Select Case iWhichInput
  402.                                                     Case cInputUp:
  403.                                                         m_arrPlayer(iPlayer).moveY = -1
  404.                                                     Case cInputDown:
  405.                                                         m_arrPlayer(iPlayer).moveY = 1
  406.                                                     Case cInputLeft:
  407.                                                         m_arrPlayer(iPlayer).moveX = -1
  408.                                                     Case cInputRight:
  409.                                                         m_arrPlayer(iPlayer).moveX = 1
  410.                                                     Case cInputButton1:
  411.                                                         If m_arrPlayer(iPlayer).lastButton1 <> TRUE Then
  412.                                                             m_arrPlayer(iPlayer).button1 = TRUE
  413.                                                         End If
  414.                                                     Case cInputButton2:
  415.                                                         If m_arrPlayer(iPlayer).lastButton2 <> TRUE Then
  416.                                                             m_arrPlayer(iPlayer).button2 = TRUE
  417.                                                         End If
  418.                                                     Case cInputButton3:
  419.                                                         If m_arrPlayer(iPlayer).lastButton3 <> TRUE Then
  420.                                                             m_arrPlayer(iPlayer).button3 = TRUE
  421.                                                         End If
  422.                                                     Case cInputButton4:
  423.                                                         If m_arrPlayer(iPlayer).lastButton4 <> TRUE Then
  424.                                                             m_arrPlayer(iPlayer).button4 = TRUE
  425.                                                         End If
  426.                                                     Case Else:
  427.                                                         '(IGNORE)
  428.                                                 End Select
  429.                                                 Exit For
  430.                                                 'end if
  431.                                             End If
  432.                                         End If
  433.                                     End If
  434.                                 Next iWhichInput
  435.                                 If bFoundWho = TRUE Then Exit For
  436.                             Next iPlayer
  437.                             ' END find who this is mapped for
  438.  
  439.                         End If
  440.                     End If
  441.                 Next iLoop
  442.  
  443.                 ' Check each axis
  444.                 For iLoop = 1 To _LastAxis(iDevice)
  445.                     If (iLoop > cMaxAxis) Then Exit For
  446.                     dblNextAxis = _Axis(iLoop)
  447.                     dblNextAxis = RoundUpDouble#(dblNextAxis, 3)
  448.  
  449.                     ' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
  450.                     ' which is prone to always give minute values and never really center on true 0.
  451.                     ' A value of 1 means my axis is pushed fully in one direction.
  452.                     ' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
  453.                     ' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
  454.  
  455.                     ' Set sensitivity:
  456.                     'These are way too sensitive for analog:
  457.                     'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
  458.                     'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
  459.                     'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .001 THEN
  460.                     ''For digital input, we'll use a big picture:
  461.                     'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= 0.75 THEN
  462.                     If Abs(dblNextAxis) <= 1 And Abs(dblNextAxis) >= 0.5 Then
  463.  
  464.                         ' WE WANT CONTINUOUS MOVEMENT (DISABLE FOR NOT)
  465.                         'if TRUE=TRUE then
  466.                         If dblNextAxis <> arrAxis(iController, iLoop) Then
  467.                             ' *****************************************************************************
  468.                             ' MOVED STICK
  469.  
  470.                             ' convert to a digital value
  471.                             If dblNextAxis < 0 Then
  472.                                 iValue = -1
  473.                             Else
  474.                                 iValue = 1
  475.                             End If
  476.  
  477.                             ' BEGIN find who this is mapped for
  478.                             bFoundWho = FALSE
  479.                             For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  480.                                 For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  481.                                     If m_arrControlMap(iPlayer, iWhichInput).device = iDevice Then
  482.                                         If m_arrControlMap(iPlayer, iWhichInput).typ = cInputAxis Then
  483.                                             If m_arrControlMap(iPlayer, iWhichInput).code = iLoop Then
  484.                                                 If m_arrControlMap(iPlayer, iWhichInput).value = iValue Then
  485.                                                     bFoundWho = TRUE
  486.                                                     Select Case iWhichInput
  487.                                                         Case cInputUp:
  488.                                                             m_arrPlayer(iPlayer).moveY = -1
  489.                                                         Case cInputDown:
  490.                                                             m_arrPlayer(iPlayer).moveY = 1
  491.                                                         Case cInputLeft:
  492.                                                             m_arrPlayer(iPlayer).moveX = -1
  493.                                                         Case cInputRight:
  494.                                                             m_arrPlayer(iPlayer).moveX = 1
  495.                                                         Case cInputButton1:
  496.                                                             If m_arrPlayer(iPlayer).lastButton1 <> TRUE Then
  497.                                                                 m_arrPlayer(iPlayer).button1 = TRUE
  498.                                                             End If
  499.                                                         Case cInputButton2:
  500.                                                             If m_arrPlayer(iPlayer).lastButton2 <> TRUE Then
  501.                                                                 m_arrPlayer(iPlayer).button2 = TRUE
  502.                                                             End If
  503.                                                         Case cInputButton3:
  504.                                                             If m_arrPlayer(iPlayer).lastButton3 <> TRUE Then
  505.                                                                 m_arrPlayer(iPlayer).button3 = TRUE
  506.                                                             End If
  507.                                                         Case cInputButton4:
  508.                                                             If m_arrPlayer(iPlayer).lastButton4 <> TRUE Then
  509.                                                                 m_arrPlayer(iPlayer).button4 = TRUE
  510.                                                             End If
  511.                                                         Case Else:
  512.                                                             '(IGNORE)
  513.                                                     End Select
  514.                                                     Exit For
  515.                                                 End If
  516.                                             End If
  517.                                         End If
  518.                                     End If
  519.                                 Next iWhichInput
  520.                                 If bFoundWho = TRUE Then Exit For
  521.                             Next iPlayer
  522.                             ' END find who this is mapped for
  523.  
  524.                         End If
  525.                     End If
  526.                 Next iLoop
  527.  
  528.             Next iController
  529.         End If
  530.         ' END CHECK FOR CONTROLLER INPUT
  531.         ' -----------------------------------------------------------------------------
  532.  
  533.         ' -----------------------------------------------------------------------------
  534.         ' BEGIN CHECK FOR KEYBOARD INPUT #1
  535.         '_KEYCLEAR: _DELAY 1
  536.         While _DeviceInput(1): Wend ' clear and update the keyboard buffer
  537.  
  538.         ' Detect changed key state
  539.         iDevice = 1 ' keyboard
  540.         For iLoop = LBound(m_arrButtonCode) To UBound(m_arrButtonCode)
  541.             iCode = m_arrButtonCode(iLoop)
  542.             If _Button(iCode) <> FALSE Then
  543.                 ' *****************************************************************************
  544.                 ' PRESSED KEYBOARD
  545.                 'PRINT "PRESSED " + m_arrButtonKey(iLoop)
  546.  
  547.                 ' BEGIN find who this is mapped for
  548.                 bFoundWho = FALSE
  549.                 For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  550.                     For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  551.                         If m_arrControlMap(iPlayer, iWhichInput).device = iDevice Then
  552.                             If m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey Then
  553.                                 'if m_arrControlMap(iPlayer, iWhichInput).code = iLoop then
  554.                                 If m_arrControlMap(iPlayer, iWhichInput).code = iCode Then
  555.                                     'if m_arrControlMap(iPlayer, iWhichInput).value = iValue then
  556.                                     bFoundWho = TRUE
  557.                                     Select Case iWhichInput
  558.                                         Case cInputUp:
  559.                                             m_arrPlayer(iPlayer).moveY = -1
  560.                                         Case cInputDown:
  561.                                             m_arrPlayer(iPlayer).moveY = 1
  562.                                         Case cInputLeft:
  563.                                             m_arrPlayer(iPlayer).moveX = -1
  564.                                         Case cInputRight:
  565.                                             m_arrPlayer(iPlayer).moveX = 1
  566.                                         Case cInputButton1:
  567.                                             If m_arrPlayer(iPlayer).lastButton1 <> TRUE Then
  568.                                                 m_arrPlayer(iPlayer).button1 = TRUE
  569.                                             End If
  570.                                         Case cInputButton2:
  571.                                             If m_arrPlayer(iPlayer).lastButton2 <> TRUE Then
  572.                                                 m_arrPlayer(iPlayer).button2 = TRUE
  573.                                             End If
  574.                                         Case cInputButton3:
  575.                                             If m_arrPlayer(iPlayer).lastButton3 <> TRUE Then
  576.                                                 m_arrPlayer(iPlayer).button3 = TRUE
  577.                                             End If
  578.                                         Case cInputButton4:
  579.                                             If m_arrPlayer(iPlayer).lastButton4 <> TRUE Then
  580.                                                 m_arrPlayer(iPlayer).button4 = TRUE
  581.                                             End If
  582.                                         Case Else:
  583.                                             '(IGNORE)
  584.                                     End Select
  585.                                     Exit For
  586.                                     'end if
  587.                                 End If
  588.                             End If
  589.                         End If
  590.                     Next iWhichInput
  591.                     If bFoundWho = TRUE Then Exit For
  592.                 Next iPlayer
  593.                 ' END find who this is mapped for
  594.  
  595.             End If
  596.         Next iLoop
  597.         ' END CHECK FOR KEYBOARD INPUT #1
  598.         ' -----------------------------------------------------------------------------
  599.  
  600.  
  601.  
  602.         ' NOW DRAW PLAYERS ON SCREEN
  603.         For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  604.  
  605.             ' MOVE RIGHT/LEFT
  606.             m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).x + m_arrPlayer(iPlayer).moveX
  607.             If m_arrPlayer(iPlayer).x < iMinX Then
  608.                 m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).xOld ' iMinX
  609.             ElseIf m_arrPlayer(iPlayer).x > iMaxX Then
  610.                 m_arrPlayer(iPlayer).x = m_arrPlayer(iPlayer).xOld ' iMaxX
  611.             End If
  612.  
  613.             ' MOVE UP/DOWN
  614.             m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).y + m_arrPlayer(iPlayer).moveY
  615.             If m_arrPlayer(iPlayer).y < iMinY Then
  616.                 m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).yOld ' iMinY
  617.             ElseIf m_arrPlayer(iPlayer).y > iMaxY Then
  618.                 m_arrPlayer(iPlayer).y = m_arrPlayer(iPlayer).yOld ' iMaxY
  619.             End If
  620.  
  621.             ' UPDATE SCREEN
  622.             '_PRINTSTRING (m_arrPlayer(iPlayer).xOld, m_arrPlayer(iPlayer).yOld), " "
  623.             '_PRINTSTRING (m_arrPlayer(iPlayer).x, m_arrPlayer(iPlayer).y), CHR$(m_arrPlayer(iPlayer).c)
  624.             PrintString m_arrPlayer(iPlayer).xOld, m_arrPlayer(iPlayer).yOld, " "
  625.             PrintString m_arrPlayer(iPlayer).x, m_arrPlayer(iPlayer).y, Chr$(m_arrPlayer(iPlayer).c)
  626.             m_arrPlayer(iPlayer).xOld = m_arrPlayer(iPlayer).x
  627.             m_arrPlayer(iPlayer).yOld = m_arrPlayer(iPlayer).y
  628.  
  629.             ' BUTTON ACTIONS
  630.             If m_arrPlayer(iPlayer).button1 = TRUE Then
  631.                 MakeSound iPlayer, 1
  632.             End If
  633.  
  634.             If m_arrPlayer(iPlayer).button2 = TRUE Then
  635.                 MakeSound iPlayer, 2
  636.             End If
  637.  
  638.             If m_arrPlayer(iPlayer).button3 = TRUE Then
  639.                 MakeSound iPlayer, 3
  640.             End If
  641.  
  642.             If m_arrPlayer(iPlayer).button4 = TRUE Then
  643.                 MakeSound iPlayer, 4
  644.             End If
  645.         Next iPlayer
  646.  
  647.         _Limit 30
  648.     Loop Until _KeyHit = 27 ' ESCAPE to quit
  649.  
  650.     _KeyClear: _Delay 1
  651.  
  652. End Function ' TestMappings$
  653.  
  654. ' /////////////////////////////////////////////////////////////////////////////
  655.  
  656. Sub MakeSound (iPlayer As Integer, iButton As Integer)
  657.     Dim note%
  658.     If iPlayer < 1 Then
  659.         iPlayer = 1
  660.     ElseIf iPlayer > 8 Then
  661.         iPlayer = 8
  662.     End If
  663.     If iButton < 1 Then
  664.         iButton = 1
  665.     ElseIf iButton > 4 Then
  666.         iButton = 4
  667.     End If
  668.  
  669.     note% = iPlayer * 100 + (iButton * 25)
  670.     If note% > 4186 Then
  671.         note% = 4186
  672.     End If
  673.     Sound note%, .75
  674. End Sub ' MakeSound
  675.  
  676. ' /////////////////////////////////////////////////////////////////////////////
  677. ' V2 prints in 2 columns.
  678.  
  679. Sub PrintControllerMap2
  680.     Dim RoutineName As String:: RoutineName = "PrintControllerMap2"
  681.     Dim iPlayer As Integer
  682.     Dim iWhichInput As Integer
  683.     Dim iCount As Integer
  684.     Dim sLine As String
  685.     Dim iHalf As Integer
  686.     Dim sColumn1 As String: sColumn1 = ""
  687.     Dim sColumn2 As String: sColumn2 = ""
  688.     ReDim arrColumn1(-1) As String
  689.     ReDim arrColumn2(-1) As String
  690.     Dim iLoop As Integer
  691.     Dim iColWidth As Integer: iColWidth = 65
  692.     Dim sValue As String
  693.     Dim in$
  694.  
  695.     ' INITIALIZE
  696.     InitKeyboardButtonCodes
  697.  
  698.     ' START OUTPUT
  699.     Print "Controller mapping:"
  700.     'Print "Player#  Input      Device#  Type     Code              Value"
  701.     '       1        button #2  x        unknown  x                 x
  702.     '       9        11         9        9        18                9
  703.     '       12345678912345678901123456789123456789123456789012345678123456789
  704.     '       12345678901234567890123456789012345678901234567890123456789012345678901234567890
  705.     '       00000000011111111112222222222333333333344444444445555555555666666666677777777778
  706.  
  707.     ' THIS IS A LAZY WAY TO GET 2 COLUMNS!
  708.     iHalf = UBound(m_arrControlMap, 1) / 2
  709.  
  710.     sLine = "Player#  Input      Device#  Type     Code              Value"
  711.     sColumn1 = sColumn1 + StrPadRight$(sLine, iColWidth) + Chr$(13)
  712.     sLine = "-------------------------------------------------------------"
  713.     sColumn1 = sColumn1 + StrPadRight$(sLine, iColWidth) + Chr$(13)
  714.     For iPlayer = LBound(m_arrControlMap, 1) To iHalf
  715.         iCount = 0
  716.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  717.             If InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" Then
  718.                 iCount = iCount + 1
  719.             End If
  720.         Next iWhichInput
  721.         If iCount > 0 Then
  722.             For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  723.                 If InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" Then
  724.                     sLine = IntPadRight$(iPlayer, 9)
  725.                     sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
  726.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
  727.                     sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
  728.  
  729.                     'sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
  730.                     If m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey Then
  731.                         sValue = GetKeyboardButtonCodeText$(m_arrControlMap(iPlayer, iWhichInput).code)
  732.                         sValue = StrPadRight$(sValue, 18)
  733.                     Else
  734.                         sValue = IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 18)
  735.                     End If
  736.                     sLine = sLine + sValue
  737.  
  738.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
  739.                     'Print sLine
  740.                     sLine = StrPadRight$(sLine, iColWidth)
  741.                     sColumn1 = sColumn1 + sLine + Chr$(13)
  742.                 End If
  743.             Next iWhichInput
  744.         Else
  745.             sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
  746.             'Print sLine
  747.             sLine = StrPadRight$(sLine, iColWidth)
  748.             sColumn1 = sColumn1 + sLine + Chr$(13)
  749.         End If
  750.     Next iPlayer
  751.  
  752.     sLine = "Player#  Input      Device#  Type     Code              Value"
  753.     sColumn2 = sColumn2 + StrPadRight$(sLine, iColWidth) + Chr$(13)
  754.     sLine = "-------------------------------------------------------------"
  755.     sColumn2 = sColumn2 + StrPadRight$(sLine, iColWidth) + Chr$(13)
  756.     For iPlayer = iHalf + 1 To UBound(m_arrControlMap, 1)
  757.         iCount = 0
  758.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  759.             If InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" Then
  760.                 iCount = iCount + 1
  761.             End If
  762.         Next iWhichInput
  763.         If iCount > 0 Then
  764.             For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  765.                 If InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" Then
  766.                     sLine = IntPadRight$(iPlayer, 9)
  767.                     sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
  768.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
  769.                     sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
  770.  
  771.                     'sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
  772.                     If m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey Then
  773.                         sValue = GetKeyboardButtonCodeText$(m_arrControlMap(iPlayer, iWhichInput).code)
  774.                         sValue = StrPadRight$(sValue, 18)
  775.                     Else
  776.                         sValue = IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 18)
  777.                     End If
  778.                     sLine = sLine + sValue
  779.  
  780.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
  781.                     'Print sLine
  782.                     sLine = StrPadRight$(sLine, iColWidth)
  783.                     sColumn2 = sColumn2 + sLine + Chr$(13)
  784.                 End If
  785.             Next iWhichInput
  786.         Else
  787.             sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
  788.             'Print sLine
  789.             sLine = StrPadRight$(sLine, iColWidth)
  790.             sColumn2 = sColumn2 + sLine + Chr$(13)
  791.         End If
  792.     Next iPlayer
  793.  
  794.     split sColumn1, Chr$(13), arrColumn1()
  795.     split sColumn2, Chr$(13), arrColumn2()
  796.     If UBound(arrColumn1) > UBound(arrColumn2) Then
  797.         iCount = UBound(arrColumn1)
  798.     Else
  799.         iCount = UBound(arrColumn2)
  800.     End If
  801.     For iLoop = 0 To iCount
  802.         sLine = ""
  803.         If UBound(arrColumn1) >= iLoop Then
  804.             sLine = sLine + arrColumn1(iLoop)
  805.         Else
  806.             sLine = sLine + String$(iColWidth, " ")
  807.         End If
  808.         sLine = sLine + "     "
  809.         If UBound(arrColumn2) >= iLoop Then
  810.             sLine = sLine + arrColumn2(iLoop)
  811.         Else
  812.             sLine = sLine + String$(iColWidth, " ")
  813.         End If
  814.         Print sLine
  815.     Next iLoop
  816. End Sub ' PrintControllerMap2
  817.  
  818. ' /////////////////////////////////////////////////////////////////////////////
  819. ' Original (simple) routine
  820.  
  821. Sub PrintControllerMap1
  822.     Dim RoutineName As String:: RoutineName = "PrintControllerMap1"
  823.     Dim iPlayer As Integer
  824.     Dim iWhichInput As Integer
  825.     Dim sLine As String
  826.     Dim iCount As Integer
  827.     Dim in$
  828.  
  829.     ' INITIALIZE
  830.     InitKeyboardButtonCodes
  831.  
  832.     ' OUTPUT MAPPING
  833.     Print "Controller mapping:"
  834.     Print "Player#  Input      Device#  Type     Code     Value"
  835.     '      1        button #2  x        unknown  x        x
  836.     '      9        11         9        9        9        9
  837.     '      12345678912345678901123456789123456789123456789123456789
  838.     '      12345678901234567890123456789012345678901234567890123456789012345678901234567890
  839.     For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  840.         iCount = 0
  841.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  842.             If InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" Then
  843.                 iCount = iCount + 1
  844.             End If
  845.         Next iWhichInput
  846.         If iCount > 0 Then
  847.             For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  848.                 If InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" Then
  849.                     sLine = IntPadRight$(iPlayer, 9)
  850.                     sLine = sLine + StrPadRight$(InputToString$(iWhichInput), 11)
  851.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).device, 9)
  852.                     sLine = sLine + StrPadRight$(InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ), 9)
  853.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).code, 9)
  854.                     sLine = sLine + IntPadRight$(m_arrControlMap(iPlayer, iWhichInput).value, 9)
  855.                     Print sLine
  856.                 End If
  857.             Next iWhichInput
  858.         Else
  859.             sLine = IntPadRight$(iPlayer, 9) + "(NONE)"
  860.             Print sLine
  861.         End If
  862.     Next iPlayer
  863. End Sub ' PrintControllerMap1
  864.  
  865. ' /////////////////////////////////////////////////////////////////////////////
  866.  
  867. Function LoadMappings$
  868.     Dim sResult As String: sResult = ""
  869.  
  870.     ' INITIALIZE
  871.     InitKeyboardButtonCodes
  872.  
  873.     ' Try loading map
  874.     sResult = LoadControllerMap$
  875.  
  876.     LoadMappings$ = sResult
  877. End Function ' LoadMappings$
  878.  
  879. ' /////////////////////////////////////////////////////////////////////////////
  880.  
  881. Function SaveMappings$
  882.     Dim sResult As String: sResult = ""
  883.     Dim sError As String: sError = ""
  884.  
  885.     ' INITIALIZE
  886.     InitKeyboardButtonCodes
  887.  
  888.     ' Try saving map
  889.     sResult = SaveControllerMap$
  890.  
  891.     SaveMappings$ = sResult
  892. End Function ' SaveMappings$
  893.  
  894.  
  895. ' /////////////////////////////////////////////////////////////////////////////
  896.  
  897. Function ViewMappings$
  898.     ' INITIALIZE
  899.     InitKeyboardButtonCodes
  900.  
  901.     PrintControllerMap2
  902.     Print
  903.     Input "PRESS <ENTER> TO CONTINUE", in$
  904.     Print
  905.     ViewMappings$ = ""
  906. End Function ' ViewMappings$
  907.  
  908. ' /////////////////////////////////////////////////////////////////////////////
  909. ' TODO: test this
  910.  
  911. Function EditMappings$
  912.     Dim RoutineName As String: RoutineName = "EditMappings$"
  913.     Dim in$
  914.     Dim iPlayer As Integer
  915.     Dim iWhichInput As Integer
  916.     Dim iDevice As Integer
  917.     Dim iType As Integer
  918.     Dim iCode As Integer
  919.     Dim iValue As Integer
  920.     Dim iItem As Integer
  921.     Dim sResult As String: sResult = ""
  922.     Dim bContinue1 As Integer: bContinue1 = TRUE
  923.     Dim bContinue2 As Integer: bContinue2 = TRUE
  924.     Dim bContinue3 As Integer: bContinue3 = TRUE
  925.     Dim bContinue4 As Integer: bContinue4 = TRUE
  926.  
  927.     ' INITIALIZE
  928.     InitKeyboardButtonCodes
  929.  
  930.     ' EDIT
  931.     Do
  932.         PrintControllerMap2
  933.         Print "To edit a mapping, enter a player number: " _
  934.             "1-" + cstr$(cMaxPlayers) + ", " + _
  935.             cstr$(cMaxPlayers+1) + ") or 0 to exit."
  936.         Input "Edit mapping for player? "; iPlayer
  937.         If iPlayer > 0 And iPlayer <= cMaxPlayers Then
  938.             bContinue2 = TRUE
  939.             Do
  940.                 Print "Editing mappings for player " + cstr$(iPlayer) + "."
  941.                 For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  942.                     Print Right$("  " + cstr$(iWhichInput), 2) + ". " + InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ)
  943.                 Next iWhichInput
  944.                 Input "Type # of control to edit or q to quit editing player."; in$
  945.                 If IsNum%(in$) Then
  946.                     iWhichInput = Val(in$)
  947.                     If iWhichInput >= LBound(m_arrControlMap, 2) And m_arrControlMap <= UBound(m_arrControlMap, 2) Then
  948.                         bContinue3 = TRUE
  949.                         Do
  950.                             Print "Settings for " + Chr$(34) + InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) + Chr$(34) + ":"
  951.                             Print "1. Device #   : " + cstr$(m_arrControlMap(iPlayer, iWhichInput).device)
  952.                             Print "2. Device type: " + InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ)
  953.                             Print "3. Input code : " + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).code))
  954.                             Print "4. Input value: " + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).value))
  955.                             Input "Change item? (1-4 or q to quit editing control)"; in$
  956.                             If IsNum%(in$) Then
  957.                                 iItem = Val(in$)
  958.                                 Select Case iItem
  959.                                     Case 1:
  960.                                         Print "Change the device number."
  961.                                         Input "Type a new device #, 0 for none, or blank to leave it unchanged"; in$
  962.                                         If IsNum%(in$) Then
  963.                                             iDevice = Val(in$)
  964.                                             m_arrControlMap(iPlayer, iWhichInput).device = iDevice
  965.                                             Print "Updated device number. Remember to save mappings when done."
  966.                                         Else
  967.                                             Print "(No change.)"
  968.                                         End If
  969.                                     Case 2:
  970.                                         bContinue4 = TRUE
  971.                                         Do
  972.                                             Print "Change the device type."
  973.                                             Print cstr$(cInputKey) + "=keyboard"
  974.                                             Print cstr$(cInputButton) + "=game controller button"
  975.                                             Print cstr$(cInputAxis) + "=game controller joystick/axis"
  976.                                             Print cstr$(cInputNone) + "=none"
  977.                                             Input "Device type or blank to leave it unchanged"; in$
  978.                                             If IsNum%(in$) Then
  979.                                                 iType = Val(in$)
  980.                                                 if iType=cInputKey or iType=cInputButton or _
  981.                                                     iType=cInputAxis or iType=cInputNone then
  982.  
  983.                                                     m_arrControlMap(iPlayer, iWhichInput).typ = iType
  984.                                                     Print "Updated device type. Remember to save mappings when done."
  985.                                                     bContinue4 = FALSE: Exit Do
  986.                                                 Else
  987.                                                     Print "Please choose one of the listed values."
  988.                                                 End If
  989.                                             Else
  990.                                                 Print "(No change.)"
  991.                                                 bContinue4 = FALSE: Exit Do
  992.                                             End If
  993.                                         Loop Until bContinue4 = FALSE
  994.                                     Case 3:
  995.                                         Print "Change the input code."
  996.                                         Input "Type a new input code, or blank to leave it unchanged"; in$
  997.                                         If IsNum%(in$) Then
  998.                                             iCode = Val(in$)
  999.                                             m_arrControlMap(iPlayer, iWhichInput).code = iCode
  1000.                                             Print "Updated input code. Remember to save mappings when done."
  1001.                                         Else
  1002.                                             Print "(No change.)"
  1003.                                         End If
  1004.                                     Case 4:
  1005.                                         Print "Change the input value."
  1006.                                         Input "Type a new input value, or blank to leave it unchanged"; in$
  1007.                                         If IsNum%(in$) Then
  1008.                                             iValue = Val(in$)
  1009.                                             m_arrControlMap(iPlayer, iWhichInput).value = value
  1010.                                             Print "Updated input value. Remember to save mappings when done."
  1011.                                         Else
  1012.                                             Print "(No change.)"
  1013.                                         End If
  1014.                                     Case Else:
  1015.                                         Print "Please choose a number between 1 and 4."
  1016.                                 End Select
  1017.                             Else
  1018.                                 bContinue3 = FALSE: Exit Do
  1019.                             End If
  1020.                         Loop Until bContinue3 = FALSE
  1021.                     Else
  1022.                         Print "Please choose a number between " + cstr$(LBound(m_arrControlMap, 2)) + " and " + cstr$(UBound(m_arrControlMap, 2)) + "."
  1023.                     End If
  1024.                 Else
  1025.                     bContinue2 = FALSE: Exit Do
  1026.                 End If
  1027.             Loop Until bContinue2 = FALSE
  1028.             If bContinue1 = FALSE Then Exit Do
  1029.         Else
  1030.             If Len(sResult) = 0 Then sResult = "(Cancelled.)"
  1031.             bContinue1 = FALSE: Exit Do
  1032.         End If
  1033.     Loop Until bContinue1 = FALSE
  1034.  
  1035.     _KeyClear: _Delay 1
  1036.  
  1037.     EditMappings$ = sResult
  1038. End Function ' EditMappings$
  1039.  
  1040. ' /////////////////////////////////////////////////////////////////////////////
  1041.  
  1042. Function ResetMapping$
  1043.     Dim RoutineName As String: RoutineName = "ResetMapping$"
  1044.     Dim in$
  1045.     Dim iPlayer As Integer
  1046.     Dim sResult As String: sResult = ""
  1047.  
  1048.     ' INITIALIZE
  1049.     InitKeyboardButtonCodes
  1050.  
  1051.     ' RESET
  1052.     Do
  1053.         PrintControllerMap2
  1054.  
  1055.         Print "To delete mapping, enter a player number: " _
  1056.             "1-" + cstr$(cMaxPlayers) + ", " + _
  1057.             cstr$(cMaxPlayers+1) + " for all, or 0 to exit."
  1058.         Input "Delete mapping for player? "; iPlayer
  1059.  
  1060.         If iPlayer > 0 And iPlayer <= cMaxPlayers Then
  1061.             Print "Delete mappings for player " + cstr$(iPlayer) + "."
  1062.             Input "Delete (y/n)"; in$: in$ = LCase$(_Trim$(in$))
  1063.             If in$ = "y" Then
  1064.                 For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  1065.                     If InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" Then
  1066.                         m_arrControlMap(iPlayer, iWhichInput).device = 0
  1067.                         m_arrControlMap(iPlayer, iWhichInput).typ = 0
  1068.                         m_arrControlMap(iPlayer, iWhichInput).code = 0
  1069.                         m_arrControlMap(iPlayer, iWhichInput).value = 0
  1070.                     End If
  1071.                 Next iWhichInput
  1072.                 sResult = "Mappings deleted for player " + cstr$(iPlayer) + "."
  1073.                 Print sResult
  1074.             End If
  1075.         ElseIf iPlayer = (cMaxPlayers + 1) Then
  1076.             Input "Delete all mappings (y/n)"; in$: in$ = LCase$(_Trim$(in$))
  1077.             If in$ = "y" Then
  1078.                 For iPlayer = 1 To cMaxPlayers
  1079.                     For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  1080.                         If InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) <> "unknown" Then
  1081.                             m_arrControlMap(iPlayer, iWhichInput).device = 0
  1082.                             m_arrControlMap(iPlayer, iWhichInput).typ = 0
  1083.                             m_arrControlMap(iPlayer, iWhichInput).code = 0
  1084.                             m_arrControlMap(iPlayer, iWhichInput).value = 0
  1085.                         End If
  1086.                     Next iWhichInput
  1087.                 Next iPlayer
  1088.                 sResult = "All mappings deleted."
  1089.                 Print sResult
  1090.             End If
  1091.         Else
  1092.             If Len(sResult) = 0 Then sResult = "(Cancelled.)"
  1093.             Exit Do
  1094.         End If
  1095.     Loop
  1096.     ResetMapping$ = sResult
  1097. End Function ' ResetMapping$
  1098.  
  1099. ' /////////////////////////////////////////////////////////////////////////////
  1100.  
  1101. Function MapInput$
  1102.     Dim RoutineName As String: RoutineName = "MapInput$"
  1103.     Dim in$
  1104.     Dim iDeviceCount As Integer
  1105.     Dim iPlayer As Integer
  1106.     Dim sResult As String
  1107.     Dim sError As String
  1108.  
  1109.     ' INITIALIZE
  1110.     InitKeyboardButtonCodes
  1111.  
  1112.     ' SET UP SCREEN
  1113.     Screen _NewImage(1280, 1024, 32): _ScreenMove 0, 0
  1114.  
  1115.     ' MAKE SURE WE HAVE DEVICES
  1116.     ' 1 is the keyboard
  1117.     ' 2 is the mouse
  1118.     ' 3 is the joystick
  1119.     ' unless someone has a strange setup with multiple mice/keyboards/ect...
  1120.     ' In that case, you can use _DEVICE$(i) to look for "KEYBOARD", "MOUSE", "JOYSTICK", if necessary.
  1121.     ' I've never actually found it necessary, but I figure it's worth mentioning, just in case...
  1122.     iDeviceCount = _Devices ' Find the number of devices on someone's system
  1123.     If iDeviceCount > 2 Then
  1124.         '' Try loading map
  1125.         'sError = LoadControllerMap$
  1126.         'if len(sError) = 0 then
  1127.         '    print "Previous controller mapping loaded."
  1128.         'else
  1129.         '    print "*******************************************************************************"
  1130.         '    print "There were errors loading the controller mapping file:"
  1131.         '    print sError
  1132.         '    print
  1133.         '    print "Try remapping - a new file will be created."
  1134.         '    print "*******************************************************************************"
  1135.         'end if
  1136.         Do
  1137.             PrintControllerMap2
  1138.             Print "To edit mapping, enter a player number (1-" + cstr$(cMaxPlayers) + ") or 0 to exit."
  1139.             Input "Get input for player? "; iPlayer
  1140.             If iPlayer > 0 And iPlayer <= cMaxPlayers Then
  1141.                 sResult = MapInput1$(iPlayer)
  1142.                 If Len(sResult) = 0 Then
  1143.                     Print "Remember to save mappings when done."
  1144.                 Else
  1145.                     Print sResult
  1146.                 End If
  1147.             Else
  1148.                 sResult = "(Cancelled.)"
  1149.                 Exit Do
  1150.             End If
  1151.         Loop
  1152.     Else
  1153.         sResult = "No controller devices found."
  1154.         Input "PRESS <ENTER> TO CONTINUE", in$
  1155.     End If
  1156.     MapInput$ = sResult
  1157.  
  1158. End Function ' MapInput$
  1159.  
  1160. ' /////////////////////////////////////////////////////////////////////////////
  1161. ' Detect controls
  1162. ' THIS VERSION SUPPORTS UPTO 8 JOYSTICKS, WITH UPTO 2 BUTTONS AND 2 AXES EACH
  1163. ' (THIS IS FOR ATARI 2600 JOYSTICKS)
  1164.  
  1165. ' The following shared arrays must be declared:
  1166. '     ReDim Shared m_arrButtonCode(1 To 99) As Long
  1167. '     ReDim Shared m_arrButtonKey(1 To 99) As String
  1168.  
  1169. Function MapInput1$ (iPlayer As Integer)
  1170.     Dim RoutineName As String:: RoutineName = "MapInput1$"
  1171.     Dim sResult As String: sResult = ""
  1172.     Dim sError As String: sError = ""
  1173.  
  1174.     Dim in$
  1175.  
  1176.     Dim iDeviceCount As Integer
  1177.     Dim iDevice As Integer
  1178.     Dim iNumControllers As Integer
  1179.     Dim iController As Integer
  1180.  
  1181.     Dim iLoop As Integer
  1182.     Dim strValue As String
  1183.     Dim strAxis As String
  1184.     Dim dblNextAxis
  1185.     Dim iCount As Long
  1186.     Dim iValue As Integer
  1187.     Dim iCode As Integer
  1188.  
  1189.     Dim arrButton(32, 16) As Integer ' number of buttons on the joystick
  1190.     Dim arrButtonNew(32, 16) As Integer ' tracks when to initialize values
  1191.     Dim arrAxis(32, 16) As Double ' number of axis on the joystick
  1192.     Dim arrAxisNew(32, 16) As Integer ' tracks when to initialize values
  1193.  
  1194.     'Dim arrInput(1 To 8) As ControlInputType
  1195.     Dim iWhichInput As Integer
  1196.     Dim bFinished As Integer
  1197.     Dim bHaveInput As Integer
  1198.     Dim bMoveNext As Integer
  1199.     Dim bCancel As Integer
  1200.     Dim iNextInput As Integer
  1201.  
  1202.     ' FOR PRINTING OUTPUT
  1203.     Dim iDigits As Integer ' # digits to display (values are truncated to this length)
  1204.     Dim iColCount As Integer
  1205.     Dim iGroupCount As Integer
  1206.     Dim sLine As String
  1207.     Dim iCols As Integer
  1208.     Dim iRows As Integer
  1209.     Dim iMaxCols As Integer
  1210.  
  1211.     ' INITIALIZE
  1212.     If Len(sError) = 0 Then
  1213.         iDigits = 4 ' 11
  1214.         iColCount = 3
  1215.         iGroupCount = 0 ' re-initialized at the top of every loop
  1216.         iCols = _Width(0) \ _FontWidth
  1217.         iRows = _Height(0) \ _FontHeight
  1218.     End If
  1219.  
  1220.     ' COUNT # OF JOYSTICKS
  1221.     ' TODO: find out the right way to count joysticks
  1222.     If Len(sError) = 0 Then
  1223.         ' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
  1224.         iDeviceCount = _Devices ' Find the number of devices on someone's system
  1225.  
  1226.         If iDeviceCount > 2 Then
  1227.             ' LIMIT # OF DEVICES, IF THERE IS A LIMIT DEFINED
  1228.             iNumControllers = iDeviceCount - 2
  1229.             If cMaxControllers > 0 Then
  1230.                 If iNumControllers > cMaxControllers Then
  1231.                     iNumControllers = cMaxControllers
  1232.                 End If
  1233.             End If
  1234.         Else
  1235.             ' ONLY 2 FOUND (KEYBOARD, MOUSE)
  1236.             sError = "No game controllers found."
  1237.         End If
  1238.     End If
  1239.  
  1240.     ' INITIALIZE CONTROLLER DATA
  1241.     If Len(sError) = 0 Then
  1242.         For iController = 1 To iNumControllers
  1243.             m_arrController(iController).buttonCount = cMaxButtons
  1244.             m_arrController(iController).axisCount = cMaxAxis
  1245.             For iLoop = 1 To cMaxButtons
  1246.                 arrButtonNew(iController, iLoop) = TRUE
  1247.             Next iLoop
  1248.             For iLoop = 1 To cMaxAxis
  1249.                 arrAxisNew(iController, iLoop) = TRUE
  1250.             Next iLoop
  1251.         Next iController
  1252.     End If
  1253.  
  1254.     ' INITIALIZE CONTROLLER INPUT
  1255.     If Len(sError) = 0 Then
  1256.         Cls
  1257.         Print "We will now detect controllers."
  1258.         Print "Do not touch any keys or game controllers during detection."
  1259.         Input "Press <ENTER> to begin"; in$
  1260.         _KeyClear: Print
  1261.         sLine = "Initializing controllers": Print sLine;
  1262.         iMaxCols = (iCols - Len(sLine)) - 1
  1263.         iCount = 0
  1264.         Do
  1265.             iCount = iCount + 1
  1266.             If iCount < iMaxCols Then
  1267.                 Print ".";
  1268.             Else
  1269.                 Print ".": Print sLine: iCount = 0
  1270.             End If
  1271.             For iController = 1 To iNumControllers
  1272.                 iDevice = iController + 2
  1273.                 While _DeviceInput(iDevice) ' clear and update the device buffer
  1274.                     For iLoop = 1 To _LastButton(iDevice)
  1275.                         If (iLoop > cMaxButtons) Then Exit For
  1276.                         m_arrController(iController).buttonCount = iLoop
  1277.                         'IF _BUTTONCHANGE(iLoop) THEN
  1278.                         '    arrButton(iController, iLoop) = _BUTTON(iLoop)
  1279.                         'END IF
  1280.                         arrButton(iController, iLoop) = FALSE
  1281.                     Next iLoop
  1282.                     For iLoop = 1 To _LastAxis(iDevice) ' this loop checks all my axis
  1283.                         If (iLoop > cMaxAxis) Then Exit For
  1284.                         m_arrController(iController).axisCount = iLoop
  1285.                         arrAxis(iController, iLoop) = 0
  1286.                     Next iLoop
  1287.                 Wend ' clear and update the device buffer
  1288.             Next iController
  1289.             _Limit 30
  1290.         Loop Until iCount > 60 ' quit after 2 seconds
  1291.         Print: Print
  1292.     End If
  1293.  
  1294.     ' WAIT FOR INPUT
  1295.     If Len(sError) = 0 Then
  1296.         Cls
  1297.         Print "Press <ESCAPE> to cancel at any time."
  1298.         Print
  1299.  
  1300.         _KeyClear: _Delay 1
  1301.         bCancel = FALSE
  1302.         bFinished = FALSE
  1303.         iLastPressed = 0
  1304.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  1305.             'print "iWhichInput=" + cstr$(iWhichInput)
  1306.             Print "Player #" + cstr$(iPlayer) + " press control for " + InputToString$(iWhichInput) + " or ESC to skip: ";
  1307.  
  1308.             ' =============================================================================
  1309.             ' BEGIN LOOK FOR NEXT INPUT
  1310.             bMoveNext = FALSE
  1311.             Do
  1312.                 ' -----------------------------------------------------------------------------
  1313.                 ' BEGIN CHECK FOR CONTROLLER INPUT
  1314.                 For iController = 1 To iNumControllers
  1315.                     iDevice = iController + 2
  1316.  
  1317.                     ' Check all devices
  1318.                     While _DeviceInput(iDevice)
  1319.  
  1320.                         ' Check each button
  1321.                         If bMoveNext = FALSE Then
  1322.                             For iLoop = 1 To _LastButton(iDevice)
  1323.                                 If (iLoop > cMaxButtons) Then Exit For
  1324.                                 'm_arrController(iController).buttonCount = iLoop
  1325.  
  1326.                                 ' update button array to indicate if a button is up or down currently.
  1327.                                 If _ButtonChange(iLoop) Then
  1328.                                     iValue = _Button(iLoop)
  1329.                                     If iValue <> arrButton(iController, iLoop) Then
  1330.                                         ' *****************************************************************************
  1331.                                         ' PRESSED BUTTON
  1332.  
  1333.                                         ' make sure this isn't already mapped
  1334.                                         bHaveInput = TRUE
  1335.                                         If iWhichInput > LBound(m_arrControlMap, 2) Then
  1336.                                             ' is input unique?
  1337.                                             For iNextInput = LBound(m_arrControlMap, 2) To iWhichInput - 1
  1338.                                                 If m_arrControlMap(iPlayer, iNextInput).device = iDevice Then
  1339.                                                     If m_arrControlMap(iPlayer, iNextInput).typ = cInputButton Then
  1340.                                                         If m_arrControlMap(iPlayer, iNextInput).code = iLoop Then
  1341.                                                             If m_arrControlMap(iPlayer, iNextInput).value = iValue Then
  1342.                                                                 bHaveInput = FALSE
  1343.                                                             End If
  1344.                                                         End If
  1345.                                                     End If
  1346.                                                 End If
  1347.                                             Next iNextInput
  1348.                                         End If
  1349.  
  1350.                                         If bHaveInput Then
  1351.                                             m_arrControlMap(iPlayer, iWhichInput).device = iDevice
  1352.                                             m_arrControlMap(iPlayer, iWhichInput).typ = cInputButton
  1353.                                             m_arrControlMap(iPlayer, iWhichInput).code = iLoop
  1354.                                             m_arrControlMap(iPlayer, iWhichInput).value = iValue
  1355.                                             bMoveNext = TRUE
  1356.                                         End If
  1357.  
  1358.                                     End If
  1359.                                 End If
  1360.                             Next iLoop
  1361.                         End If
  1362.  
  1363.                         ' Check each axis
  1364.                         If bMoveNext = FALSE Then
  1365.                             For iLoop = 1 To _LastAxis(iDevice)
  1366.                                 If (iLoop > cMaxAxis) Then Exit For
  1367.                                 'm_arrController(iController).axisCount = iLoop
  1368.  
  1369.                                 dblNextAxis = _Axis(iLoop)
  1370.                                 dblNextAxis = RoundUpDouble#(dblNextAxis, 3)
  1371.  
  1372.                                 ' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
  1373.                                 ' which is prone to always give minute values and never really center on true 0.
  1374.                                 ' A value of 1 means my axis is pushed fully in one direction.
  1375.                                 ' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
  1376.                                 ' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
  1377.  
  1378.                                 'These are way too sensitive for analog:
  1379.                                 'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
  1380.                                 'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
  1381.                                 'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .001 THEN
  1382.  
  1383.                                 'For digital input, we'll use a big picture:
  1384.                                 If Abs(dblNextAxis) <= 1 And Abs(dblNextAxis) >= 0.75 Then
  1385.                                     If dblNextAxis <> arrAxis(iController, iLoop) Then
  1386.                                         ' *****************************************************************************
  1387.                                         ' MOVED STICK
  1388.  
  1389.                                         ' convert to a digital value
  1390.                                         If dblNextAxis < 0 Then
  1391.                                             iValue = -1
  1392.                                         Else
  1393.                                             iValue = 1
  1394.                                         End If
  1395.  
  1396.                                         ' make sure this isn't already mapped
  1397.                                         bHaveInput = TRUE
  1398.                                         If iWhichInput > LBound(m_arrControlMap, 2) Then
  1399.                                             ' is input unique?
  1400.                                             For iNextInput = LBound(m_arrControlMap, 2) To iWhichInput - 1
  1401.                                                 If m_arrControlMap(iPlayer, iNextInput).device = iDevice Then
  1402.                                                     If m_arrControlMap(iPlayer, iNextInput).typ = cInputAxis Then
  1403.                                                         If m_arrControlMap(iPlayer, iNextInput).code = iLoop Then
  1404.                                                             If m_arrControlMap(iPlayer, iNextInput).value = iValue Then
  1405.                                                                 bHaveInput = FALSE
  1406.                                                             End If
  1407.                                                         End If
  1408.                                                     End If
  1409.                                                 End If
  1410.                                             Next iNextInput
  1411.                                         End If
  1412.  
  1413.                                         If bHaveInput Then
  1414.                                             m_arrControlMap(iPlayer, iWhichInput).device = iDevice
  1415.                                             m_arrControlMap(iPlayer, iWhichInput).typ = cInputAxis
  1416.                                             m_arrControlMap(iPlayer, iWhichInput).code = iLoop
  1417.                                             m_arrControlMap(iPlayer, iWhichInput).value = iValue
  1418.                                             bMoveNext = TRUE
  1419.                                         End If
  1420.  
  1421.                                     End If
  1422.                                 End If
  1423.                             Next iLoop
  1424.                         End If
  1425.  
  1426.                     Wend ' clear and update the device buffer
  1427.  
  1428.                 Next iController
  1429.                 ' END CHECK FOR CONTROLLER INPUT
  1430.                 ' -----------------------------------------------------------------------------
  1431.  
  1432.                 ' -----------------------------------------------------------------------------
  1433.                 ' BEGIN CHECK FOR KEYBOARD INPUT #1
  1434.                 If bMoveNext = FALSE Then
  1435.                     '_KEYCLEAR: _DELAY 1
  1436.                     While _DeviceInput(1): Wend ' clear and update the keyboard buffer
  1437.  
  1438.                     ' Detect changed key state
  1439.                     For iLoop = LBound(m_arrButtonCode) To UBound(m_arrButtonCode)
  1440.                         iCode = m_arrButtonCode(iLoop)
  1441.                         If _Button(iCode) <> FALSE Then
  1442.                             ' *****************************************************************************
  1443.                             ' PRESSED KEYBOARD
  1444.                             'PRINT "PRESSED " + m_arrButtonKey(iLoop)
  1445.  
  1446.                             ' make sure this isn't already mapped
  1447.                             bHaveInput = TRUE
  1448.                             If iWhichInput > LBound(m_arrControlMap, 2) Then
  1449.                                 ' is input unique?
  1450.                                 For iNextInput = LBound(m_arrControlMap, 2) To iWhichInput - 1
  1451.                                     If m_arrControlMap(iPlayer, iNextInput).device = 1 Then ' .device 1 = keyboard
  1452.                                         If m_arrControlMap(iPlayer, iNextInput).typ = cInputKey Then
  1453.                                             If m_arrControlMap(iPlayer, iNextInput).code = iCode Then
  1454.                                                 'if m_arrControlMap(iPlayer, iNextInput).value = TRUE then
  1455.                                                 bHaveInput = FALSE
  1456.                                                 'end if
  1457.                                             End If
  1458.                                         End If
  1459.                                     End If
  1460.                                 Next iNextInput
  1461.                             End If
  1462.  
  1463.                             If bHaveInput Then
  1464.                                 m_arrControlMap(iPlayer, iWhichInput).device = 1 ' .device 1 = keyboard
  1465.                                 m_arrControlMap(iPlayer, iWhichInput).typ = cInputKey
  1466.                                 m_arrControlMap(iPlayer, iWhichInput).code = iCode
  1467.                                 m_arrControlMap(iPlayer, iWhichInput).value = TRUE
  1468.                                 bMoveNext = TRUE
  1469.                             End If
  1470.  
  1471.                         End If
  1472.                     Next iLoop
  1473.                 End If
  1474.                 ' END CHECK FOR KEYBOARD INPUT #1
  1475.                 ' -----------------------------------------------------------------------------
  1476.  
  1477.                 If bMoveNext = TRUE Then Exit Do
  1478.                 _Limit 30
  1479.             Loop Until _KeyHit = 27 ' ESCAPE to quit
  1480.             ' END LOOK FOR NEXT INPUT
  1481.             ' =============================================================================
  1482.  
  1483.             If bMoveNext = TRUE Then
  1484.                 Print "Device #" + cstr$(m_arrControlMap(iPlayer, iWhichInput).device) + " " + _
  1485.                     InputTypeToString$(m_arrControlMap(iPlayer, iWhichInput).typ) + " " + _
  1486.                     cstr$(m_arrControlMap(iPlayer, iWhichInput).code) + " = " + _
  1487.                     cstr$(m_arrControlMap(iPlayer, iWhichInput).value)
  1488.             Else
  1489.                 Print "(Skipped)"
  1490.                 bCancel = TRUE
  1491.                 bFinished = TRUE
  1492.             End If
  1493.  
  1494.             If bFinished = TRUE Then Exit For
  1495.         Next iWhichInput
  1496.     End If
  1497.  
  1498.     If Len(sError) > 0 Then
  1499.         sResult = "ERRORS: " + sError
  1500.     End If
  1501.  
  1502.     _KeyClear: _Delay 1
  1503.     MapInput1$ = sResult
  1504. End Function ' MapInput1$
  1505.  
  1506. ' /////////////////////////////////////////////////////////////////////////////
  1507.  
  1508. Function InputToString$ (iCode As Integer)
  1509.     Select Case iCode
  1510.         Case cInputUp:
  1511.             InputToString$ = "up"
  1512.         Case cInputDown:
  1513.             InputToString$ = "down"
  1514.         Case cInputLeft:
  1515.             InputToString$ = "left"
  1516.         Case cInputRight:
  1517.             InputToString$ = "right"
  1518.         Case cInputButton1:
  1519.             InputToString$ = "button #1"
  1520.         Case cInputButton2:
  1521.             InputToString$ = "button #2"
  1522.         Case cInputButton3:
  1523.             InputToString$ = "button #3"
  1524.         Case cInputButton4:
  1525.             InputToString$ = "button #4"
  1526.         Case Else:
  1527.             InputToString$ = "unknown"
  1528.     End Select
  1529. End Function ' InputToString$
  1530.  
  1531. ' /////////////////////////////////////////////////////////////////////////////
  1532.  
  1533. Function InputTypeToString$ (iCode As Integer)
  1534.     Select Case iCode
  1535.         Case cInputNone:
  1536.             InputTypeToString$ = "none"
  1537.         Case cInputKey:
  1538.             InputTypeToString$ = "key"
  1539.         Case cInputButton:
  1540.             InputTypeToString$ = "button"
  1541.         Case cInputAxis:
  1542.             InputTypeToString$ = "axis"
  1543.         Case Else:
  1544.             InputTypeToString$ = "unknown"
  1545.     End Select
  1546. End Function ' InputTypeToString$
  1547.  
  1548. ' /////////////////////////////////////////////////////////////////////////////
  1549. ' METHOD v2 = faster
  1550.  
  1551. Function GetKeyboardButtonCodeText$ (iCode As Integer)
  1552.     Dim sResult As String: sResult = ""
  1553.     If LBound(m_arrButtonKeyDesc) <= iCode Then
  1554.         If UBound(m_arrButtonKeyDesc) >= iCode Then
  1555.             sResult = m_arrButtonKeyDesc(iCode)
  1556.         End If
  1557.     End If
  1558.     If Len(sResult) = 0 Then
  1559.         sResult = _Trim$(Str$(iCode)) + " (?)"
  1560.     End If
  1561.     GetKeyboardButtonCodeText$ = sResult
  1562. End Function ' GetKeyboardButtonCodeText$
  1563.  
  1564. ' /////////////////////////////////////////////////////////////////////////////
  1565. ' METHOD v2
  1566. ' Faster lookup - a dictionary with a hash lookup would be best
  1567. ' but this is a quick way to do it since the values never change.
  1568.  
  1569. ' The following shared arrays must be declared:
  1570. '     ReDim Shared m_arrButtonCode(1 To 99) As Long
  1571. '     ReDim Shared m_arrButtonKey(1 To 99) As String
  1572. '     ReDim Shared m_arrButtonKeyDesc(0 To 512) As String
  1573.  
  1574. Sub InitKeyboardButtonCodes ()
  1575.     Dim iLoop As Integer
  1576.  
  1577.     ' CODE(S) DETECTED WITH _BUTTON:
  1578.     m_arrButtonCode(1) = 2: m_arrButtonKey(1) = "Esc"
  1579.     m_arrButtonCode(2) = 60: m_arrButtonKey(2) = "F1"
  1580.     m_arrButtonCode(3) = 61: m_arrButtonKey(3) = "F2"
  1581.     m_arrButtonCode(4) = 62: m_arrButtonKey(4) = "F3"
  1582.     m_arrButtonCode(5) = 63: m_arrButtonKey(5) = "F4"
  1583.     m_arrButtonCode(6) = 64: m_arrButtonKey(6) = "F5"
  1584.     m_arrButtonCode(7) = 65: m_arrButtonKey(7) = "F6"
  1585.     m_arrButtonCode(8) = 66: m_arrButtonKey(8) = "F7"
  1586.     m_arrButtonCode(9) = 67: m_arrButtonKey(9) = "F8"
  1587.     m_arrButtonCode(10) = 68: m_arrButtonKey(10) = "F9"
  1588.     m_arrButtonCode(11) = 88: m_arrButtonKey(11) = "F11"
  1589.     m_arrButtonCode(12) = 89: m_arrButtonKey(12) = "F12"
  1590.     m_arrButtonCode(13) = 42: m_arrButtonKey(13) = "Tilde"
  1591.     m_arrButtonCode(14) = 3: m_arrButtonKey(14) = "1"
  1592.     m_arrButtonCode(15) = 4: m_arrButtonKey(15) = "2"
  1593.     m_arrButtonCode(16) = 5: m_arrButtonKey(16) = "3"
  1594.     m_arrButtonCode(17) = 6: m_arrButtonKey(17) = "4"
  1595.     m_arrButtonCode(18) = 7: m_arrButtonKey(18) = "5"
  1596.     m_arrButtonCode(19) = 8: m_arrButtonKey(19) = "6"
  1597.     m_arrButtonCode(20) = 9: m_arrButtonKey(20) = "7"
  1598.     m_arrButtonCode(21) = 10: m_arrButtonKey(21) = "8"
  1599.     m_arrButtonCode(22) = 11: m_arrButtonKey(22) = "9"
  1600.     m_arrButtonCode(23) = 12: m_arrButtonKey(23) = "0"
  1601.     m_arrButtonCode(24) = 13: m_arrButtonKey(24) = "Minus"
  1602.     m_arrButtonCode(25) = 14: m_arrButtonKey(25) = "Equal"
  1603.     m_arrButtonCode(26) = 15: m_arrButtonKey(26) = "BkSp"
  1604.     m_arrButtonCode(27) = 16: m_arrButtonKey(27) = "Tab"
  1605.     m_arrButtonCode(28) = 17: m_arrButtonKey(28) = "Q"
  1606.     m_arrButtonCode(29) = 18: m_arrButtonKey(29) = "W"
  1607.     m_arrButtonCode(30) = 19: m_arrButtonKey(30) = "E"
  1608.     m_arrButtonCode(31) = 20: m_arrButtonKey(31) = "R"
  1609.     m_arrButtonCode(32) = 21: m_arrButtonKey(32) = "T"
  1610.     m_arrButtonCode(33) = 22: m_arrButtonKey(33) = "Y"
  1611.     m_arrButtonCode(34) = 23: m_arrButtonKey(34) = "U"
  1612.     m_arrButtonCode(35) = 24: m_arrButtonKey(35) = "I"
  1613.     m_arrButtonCode(36) = 25: m_arrButtonKey(36) = "O"
  1614.     m_arrButtonCode(37) = 26: m_arrButtonKey(37) = "P"
  1615.     m_arrButtonCode(38) = 27: m_arrButtonKey(38) = "BracketLeft"
  1616.     m_arrButtonCode(39) = 28: m_arrButtonKey(39) = "BracketRight"
  1617.     m_arrButtonCode(40) = 44: m_arrButtonKey(40) = "Backslash"
  1618.     m_arrButtonCode(41) = 59: m_arrButtonKey(41) = "CapsLock"
  1619.     m_arrButtonCode(42) = 31: m_arrButtonKey(42) = "A"
  1620.     m_arrButtonCode(43) = 32: m_arrButtonKey(43) = "S"
  1621.     m_arrButtonCode(44) = 33: m_arrButtonKey(44) = "D"
  1622.     m_arrButtonCode(45) = 34: m_arrButtonKey(45) = "F"
  1623.     m_arrButtonCode(46) = 35: m_arrButtonKey(46) = "G"
  1624.     m_arrButtonCode(47) = 36: m_arrButtonKey(47) = "H"
  1625.     m_arrButtonCode(48) = 37: m_arrButtonKey(48) = "J"
  1626.     m_arrButtonCode(49) = 38: m_arrButtonKey(49) = "K"
  1627.     m_arrButtonCode(50) = 39: m_arrButtonKey(50) = "L"
  1628.     m_arrButtonCode(51) = 40: m_arrButtonKey(51) = "Semicolon"
  1629.     m_arrButtonCode(52) = 41: m_arrButtonKey(52) = "Apostrophe"
  1630.     m_arrButtonCode(53) = 29: m_arrButtonKey(53) = "Enter"
  1631.     m_arrButtonCode(54) = 43: m_arrButtonKey(54) = "ShiftLeft"
  1632.     m_arrButtonCode(55) = 45: m_arrButtonKey(55) = "Z"
  1633.     m_arrButtonCode(56) = 46: m_arrButtonKey(56) = "X"
  1634.     m_arrButtonCode(57) = 47: m_arrButtonKey(57) = "C"
  1635.     m_arrButtonCode(58) = 48: m_arrButtonKey(58) = "V"
  1636.     m_arrButtonCode(59) = 49: m_arrButtonKey(59) = "B"
  1637.     m_arrButtonCode(60) = 50: m_arrButtonKey(60) = "N"
  1638.     m_arrButtonCode(61) = 51: m_arrButtonKey(61) = "M"
  1639.     m_arrButtonCode(62) = 52: m_arrButtonKey(62) = "Comma"
  1640.     m_arrButtonCode(63) = 53: m_arrButtonKey(63) = "Period"
  1641.     m_arrButtonCode(64) = 54: m_arrButtonKey(64) = "Slash"
  1642.     m_arrButtonCode(65) = 55: m_arrButtonKey(65) = "ShiftRight"
  1643.     m_arrButtonCode(66) = 30: m_arrButtonKey(66) = "CtrlLeft"
  1644.     m_arrButtonCode(67) = 348: m_arrButtonKey(67) = "WinLeft"
  1645.     m_arrButtonCode(68) = 58: m_arrButtonKey(68) = "Spacebar"
  1646.     m_arrButtonCode(69) = 349: m_arrButtonKey(69) = "WinRight"
  1647.     m_arrButtonCode(70) = 350: m_arrButtonKey(70) = "Menu"
  1648.     m_arrButtonCode(71) = 286: m_arrButtonKey(71) = "CtrlRight"
  1649.     m_arrButtonCode(72) = 339: m_arrButtonKey(72) = "Ins"
  1650.     m_arrButtonCode(73) = 328: m_arrButtonKey(73) = "Home"
  1651.     m_arrButtonCode(74) = 330: m_arrButtonKey(74) = "PgUp"
  1652.     m_arrButtonCode(75) = 340: m_arrButtonKey(75) = "Del"
  1653.     m_arrButtonCode(76) = 336: m_arrButtonKey(76) = "End"
  1654.     m_arrButtonCode(77) = 338: m_arrButtonKey(77) = "PgDn"
  1655.     m_arrButtonCode(78) = 329: m_arrButtonKey(78) = "Up"
  1656.     m_arrButtonCode(79) = 332: m_arrButtonKey(79) = "Left"
  1657.     m_arrButtonCode(80) = 337: m_arrButtonKey(80) = "Down"
  1658.     m_arrButtonCode(81) = 334: m_arrButtonKey(81) = "Right"
  1659.     m_arrButtonCode(82) = 71: m_arrButtonKey(82) = "ScrollLock"
  1660.     m_arrButtonCode(83) = 326: m_arrButtonKey(83) = "NumLock"
  1661.     m_arrButtonCode(84) = 310: m_arrButtonKey(84) = "KeypadSlash"
  1662.     m_arrButtonCode(85) = 56: m_arrButtonKey(85) = "KeypadMultiply"
  1663.     m_arrButtonCode(86) = 75: m_arrButtonKey(86) = "KeypadMinus"
  1664.     m_arrButtonCode(87) = 72: m_arrButtonKey(87) = "Keypad7Home"
  1665.     m_arrButtonCode(88) = 73: m_arrButtonKey(88) = "Keypad8Up"
  1666.     m_arrButtonCode(89) = 74: m_arrButtonKey(89) = "Keypad9PgUp"
  1667.     m_arrButtonCode(90) = 79: m_arrButtonKey(90) = "KeypadPlus"
  1668.     m_arrButtonCode(91) = 76: m_arrButtonKey(91) = "Keypad4Left"
  1669.     m_arrButtonCode(92) = 77: m_arrButtonKey(92) = "Keypad5"
  1670.     m_arrButtonCode(93) = 78: m_arrButtonKey(93) = "Keypad6Right"
  1671.     m_arrButtonCode(94) = 80: m_arrButtonKey(94) = "Keypad1End"
  1672.     m_arrButtonCode(95) = 81: m_arrButtonKey(95) = "Keypad2Down"
  1673.     m_arrButtonCode(96) = 82: m_arrButtonKey(96) = "Keypad3PgDn"
  1674.     m_arrButtonCode(97) = 285: m_arrButtonKey(97) = "KeypadEnter"
  1675.     m_arrButtonCode(98) = 83: m_arrButtonKey(98) = "Keypad0Ins"
  1676.     m_arrButtonCode(99) = 84: m_arrButtonKey(99) = "KeypadPeriodDel"
  1677.  
  1678.     ' not sure if this works:
  1679.     '' CODE(S) DETECTED WITH _KEYDOWN:
  1680.     'm_arrButtonCode(100) = -1 : m_arrButtonCode(100) = "F10"
  1681.  
  1682.     ' not sure if this works:
  1683.     '' CODE(S) DETECTED WITH _KEYHIT:
  1684.     'm_arrButtonCode(101) = -2 : m_arrButtonCode(101) = "AltLeft"
  1685.     'm_arrButtonCode(102) = -3 : m_arrButtonCode(102) = "AltRight"
  1686.  
  1687.     ' DESCRIPTIONS BY KEYCODE
  1688.     For iLoop = LBound(m_arrButtonKeyDesc) To UBound(m_arrButtonKeyDesc)
  1689.         m_arrButtonKeyDesc(iLoop) = ""
  1690.     Next iLoop
  1691.     m_arrButtonKeyDesc(2) = "Esc"
  1692.     m_arrButtonKeyDesc(60) = "F1"
  1693.     m_arrButtonKeyDesc(61) = "F2"
  1694.     m_arrButtonKeyDesc(62) = "F3"
  1695.     m_arrButtonKeyDesc(63) = "F4"
  1696.     m_arrButtonKeyDesc(64) = "F5"
  1697.     m_arrButtonKeyDesc(65) = "F6"
  1698.     m_arrButtonKeyDesc(66) = "F7"
  1699.     m_arrButtonKeyDesc(67) = "F8"
  1700.     m_arrButtonKeyDesc(68) = "F9"
  1701.     m_arrButtonKeyDesc(88) = "F11"
  1702.     m_arrButtonKeyDesc(89) = "F12"
  1703.     m_arrButtonKeyDesc(42) = "Tilde"
  1704.     m_arrButtonKeyDesc(3) = "1"
  1705.     m_arrButtonKeyDesc(4) = "2"
  1706.     m_arrButtonKeyDesc(5) = "3"
  1707.     m_arrButtonKeyDesc(6) = "4"
  1708.     m_arrButtonKeyDesc(7) = "5"
  1709.     m_arrButtonKeyDesc(8) = "6"
  1710.     m_arrButtonKeyDesc(9) = "7"
  1711.     m_arrButtonKeyDesc(10) = "8"
  1712.     m_arrButtonKeyDesc(11) = "9"
  1713.     m_arrButtonKeyDesc(12) = "0"
  1714.     m_arrButtonKeyDesc(13) = "Minus"
  1715.     m_arrButtonKeyDesc(14) = "Equal"
  1716.     m_arrButtonKeyDesc(15) = "BkSp"
  1717.     m_arrButtonKeyDesc(16) = "Tab"
  1718.     m_arrButtonKeyDesc(17) = "Q"
  1719.     m_arrButtonKeyDesc(18) = "W"
  1720.     m_arrButtonKeyDesc(19) = "E"
  1721.     m_arrButtonKeyDesc(20) = "R"
  1722.     m_arrButtonKeyDesc(21) = "T"
  1723.     m_arrButtonKeyDesc(22) = "Y"
  1724.     m_arrButtonKeyDesc(23) = "U"
  1725.     m_arrButtonKeyDesc(24) = "I"
  1726.     m_arrButtonKeyDesc(25) = "O"
  1727.     m_arrButtonKeyDesc(26) = "P"
  1728.     m_arrButtonKeyDesc(27) = "BracketLeft"
  1729.     m_arrButtonKeyDesc(28) = "BracketRight"
  1730.     m_arrButtonKeyDesc(44) = "Backslash"
  1731.     m_arrButtonKeyDesc(59) = "CapsLock"
  1732.     m_arrButtonKeyDesc(31) = "A"
  1733.     m_arrButtonKeyDesc(32) = "S"
  1734.     m_arrButtonKeyDesc(33) = "D"
  1735.     m_arrButtonKeyDesc(34) = "F"
  1736.     m_arrButtonKeyDesc(35) = "G"
  1737.     m_arrButtonKeyDesc(36) = "H"
  1738.     m_arrButtonKeyDesc(37) = "J"
  1739.     m_arrButtonKeyDesc(38) = "K"
  1740.     m_arrButtonKeyDesc(39) = "L"
  1741.     m_arrButtonKeyDesc(40) = "Semicolon"
  1742.     m_arrButtonKeyDesc(41) = "Apostrophe"
  1743.     m_arrButtonKeyDesc(29) = "Enter"
  1744.     m_arrButtonKeyDesc(43) = "ShiftLeft"
  1745.     m_arrButtonKeyDesc(45) = "Z"
  1746.     m_arrButtonKeyDesc(46) = "X"
  1747.     m_arrButtonKeyDesc(47) = "C"
  1748.     m_arrButtonKeyDesc(48) = "V"
  1749.     m_arrButtonKeyDesc(49) = "B"
  1750.     m_arrButtonKeyDesc(50) = "N"
  1751.     m_arrButtonKeyDesc(51) = "M"
  1752.     m_arrButtonKeyDesc(52) = "Comma"
  1753.     m_arrButtonKeyDesc(53) = "Period"
  1754.     m_arrButtonKeyDesc(54) = "Slash"
  1755.     m_arrButtonKeyDesc(55) = "ShiftRight"
  1756.     m_arrButtonKeyDesc(30) = "CtrlLeft"
  1757.     m_arrButtonKeyDesc(348) = "WinLeft"
  1758.     m_arrButtonKeyDesc(58) = "Spacebar"
  1759.     m_arrButtonKeyDesc(349) = "WinRight"
  1760.     m_arrButtonKeyDesc(350) = "Menu"
  1761.     m_arrButtonKeyDesc(286) = "CtrlRight"
  1762.     m_arrButtonKeyDesc(339) = "Ins"
  1763.     m_arrButtonKeyDesc(328) = "Home"
  1764.     m_arrButtonKeyDesc(330) = "PgUp"
  1765.     m_arrButtonKeyDesc(340) = "Del"
  1766.     m_arrButtonKeyDesc(336) = "End"
  1767.     m_arrButtonKeyDesc(338) = "PgDn"
  1768.     m_arrButtonKeyDesc(329) = "Up"
  1769.     m_arrButtonKeyDesc(332) = "Left"
  1770.     m_arrButtonKeyDesc(337) = "Down"
  1771.     m_arrButtonKeyDesc(334) = "Right"
  1772.     m_arrButtonKeyDesc(71) = "ScrollLock"
  1773.     m_arrButtonKeyDesc(326) = "NumLock"
  1774.     m_arrButtonKeyDesc(310) = "KeypadSlash"
  1775.     m_arrButtonKeyDesc(56) = "KeypadMultiply"
  1776.     m_arrButtonKeyDesc(75) = "KeypadMinus"
  1777.     m_arrButtonKeyDesc(72) = "Keypad7Home"
  1778.     m_arrButtonKeyDesc(73) = "Keypad8Up"
  1779.     m_arrButtonKeyDesc(74) = "Keypad9PgUp"
  1780.     m_arrButtonKeyDesc(79) = "KeypadPlus"
  1781.     m_arrButtonKeyDesc(76) = "Keypad4Left"
  1782.     m_arrButtonKeyDesc(77) = "Keypad5"
  1783.     m_arrButtonKeyDesc(78) = "Keypad6Right"
  1784.     m_arrButtonKeyDesc(80) = "Keypad1End"
  1785.     m_arrButtonKeyDesc(81) = "Keypad2Down"
  1786.     m_arrButtonKeyDesc(82) = "Keypad3PgDn"
  1787.     m_arrButtonKeyDesc(285) = "KeypadEnter"
  1788.     m_arrButtonKeyDesc(83) = "Keypad0Ins"
  1789.     m_arrButtonKeyDesc(84) = "KeypadPeriodDel"
  1790. End Sub ' InitKeyboardButtonCodes
  1791.  
  1792. ' /////////////////////////////////////////////////////////////////////////////
  1793. ' not sure if this works
  1794.  
  1795. ' Returns TRUE if the F10 key is held down.
  1796. ' We use _KEYDOWN for this because _BUTTON doesn't detect F10.
  1797.  
  1798. ' Constant must be declared globally:
  1799. ' Const c_iKeyDown_F10 = 17408
  1800.  
  1801. Function KeydownF10%
  1802.     Dim iCode As Long
  1803.     '_KEYCLEAR: _DELAY 1
  1804.     If _KeyDown(c_iKeyDown_F10) = TRUE Then
  1805.         KeydownF10% = TRUE
  1806.     Else
  1807.         KeydownF10% = FALSE
  1808.     End If
  1809.     '_KEYCLEAR
  1810. End Function ' KeydownF10%
  1811.  
  1812. ' /////////////////////////////////////////////////////////////////////////////
  1813. ' not sure if this works
  1814.  
  1815. ' Returns TRUE if the left ALT key is held down.
  1816. ' We use _KEYHIT for this because _BUTTON doesn't detect ALT.
  1817.  
  1818. ' Constant must be declared globally:
  1819. ' Const c_iKeyHit_AltLeft = -30764
  1820.  
  1821. Function KeyhitAltLeft%
  1822.     '_KEYCLEAR: _DELAY 1
  1823.     If _KeyHit = c_iKeyHit_AltLeft Then
  1824.         KeyhitAltLeft% = TRUE
  1825.     Else
  1826.         KeyhitAltLeft% = FALSE
  1827.     End If
  1828.     '_KEYCLEAR
  1829. End Function ' KeyhitAltLeft%
  1830.  
  1831. ' /////////////////////////////////////////////////////////////////////////////
  1832. ' not sure if this works
  1833.  
  1834. ' Returns TRUE if the right ALT key is held down.
  1835. ' We use _KEYHIT for this because _BUTTON doesn't detect ALT.
  1836.  
  1837. ' Constant must be declared globally:
  1838. ' Const c_iKeyHit_AltRight = -30765
  1839.  
  1840. Function KeyhitAltRight%
  1841.     '_KEYCLEAR: _DELAY 1
  1842.     If _KeyHit = c_iKeyHit_AltRight Then
  1843.         KeyhitAltRight% = TRUE
  1844.     Else
  1845.         KeyhitAltRight% = FALSE
  1846.     End If
  1847.     '_KEYCLEAR
  1848. End Function ' KeyhitAltRight%
  1849.  
  1850. ' /////////////////////////////////////////////////////////////////////////////
  1851. ' DEVICES Button
  1852. ' _LASTBUTTON(1) keyboards will normally return 512 buttons. One button is read per loop through all numbers.
  1853. ' _BUTTONCHANGE(number) returns -1 when pressed, 1 when released and 0 when there is no event since the last read.
  1854. ' _BUTTON(number) returns -1 when a button is pressed and 0 when released
  1855.  
  1856. ' Detects most keys (where the codes are documented?)
  1857.  
  1858. ' However, does not seem to detect:
  1859. ' Key             Use
  1860. ' ---             ---
  1861. ' F10             Function KeydownF10%
  1862. ' Left Alt        Function KeyhitAltLeft%
  1863. ' Right Alt       Function KeyhitAltRight%
  1864. ' Print Screen    (system API call?)
  1865. ' Pause/Break     (system API call?)
  1866.  
  1867. Function KeyPressed% (iCode As Integer)
  1868.     '_KEYCLEAR: _DELAY 1
  1869.     While _DeviceInput(1): Wend ' clear and update the keyboard buffer
  1870.     If _Button(iCode) <> FALSE Then
  1871.         KeyPressed% = TRUE
  1872.     Else
  1873.         KeyPressed% = FALSE
  1874.     End If
  1875.     '_KEYCLEAR
  1876. End Function ' KeyPressed%
  1877.  
  1878. ' /////////////////////////////////////////////////////////////////////////////
  1879.  
  1880. Function TestJoysticks$
  1881.     Dim RoutineName As String: RoutineName = "TestJoysticks$"
  1882.     Dim iDeviceCount As Integer
  1883.     Dim sResult As String
  1884.  
  1885.     ' 1 is the keyboard
  1886.     ' 2 is the mouse
  1887.     ' 3 is the joystick
  1888.     ' unless someone has a strange setup with multiple mice/keyboards/ect...
  1889.     ' In that case, you can use _DEVICE$(i) to look for "KEYBOARD", "MOUSE", "JOYSTICK", if necessary.
  1890.     ' I've never actually found it necessary, but I figure it's worth mentioning, just in case...
  1891.  
  1892.     iDeviceCount = _Devices ' Find the number of devices on someone's system
  1893.     If iDeviceCount > 2 Then
  1894.         TestJoysticks1
  1895.         sResult = ""
  1896.     Else
  1897.         sResult = "No joysticks found."
  1898.     End If
  1899.  
  1900.     _KeyClear
  1901.  
  1902.     TestJoysticks$ = sResult
  1903. End Function ' TestJoysticks$
  1904.  
  1905. ' /////////////////////////////////////////////////////////////////////////////
  1906. ' Reads controllers and displays values on screen.
  1907.  
  1908. ' Currently this is set up to support up to 8 joysticks,
  1909. ' with upto 4 buttons and 2 axes each
  1910. ' Testing with an old USB Logitech RumblePad 2
  1911. ' and Atari 2600 joysticks plugged into using
  1912. ' iCode Atari Joystick, Paddle, Driving to USB Adapter 4 ports
  1913.  
  1914. Sub TestJoysticks1 ()
  1915.     Dim RoutineName As String:: RoutineName = "TestJoysticks1"
  1916.  
  1917.     Dim in$
  1918.     Dim iDeviceCount As Integer
  1919.     Dim iDevice As Integer
  1920.  
  1921.     Dim arrButton(32, 16) As Integer ' number of buttons on the joystick
  1922.     Dim arrButtonMin(32, 16) As Integer ' stores the minimum value read
  1923.     Dim arrButtonMax(32, 16) As Integer ' stores the maximum value read
  1924.     Dim arrAxis(32, 16) As Double ' number of axis on the joystick
  1925.     Dim arrAxisMin(32, 16) As Double ' stores the minimum value read
  1926.     Dim arrAxisMax(32, 16) As Double ' stores the maximum value read
  1927.     Dim arrAxisAvg(32, 16) As Double ' stores the average value read in the last few measurements
  1928.     Dim arrButtonNew(32, 16) As Integer ' tracks when to initialize values
  1929.     Dim arrAxisNew(32, 16) As Integer ' tracks when to initialize values
  1930.  
  1931.     Dim arrController(8) As ControllerType ' holds info for each player
  1932.     Dim iNumControllers As Integer
  1933.     Dim iController As Integer
  1934.     Dim iNextY As Integer
  1935.     Dim iNextX As Integer
  1936.     Dim iNextC As Integer
  1937.     Dim iLoop As Integer
  1938.     Dim iDigits As Integer ' # digits to display (values are truncated to this length)
  1939.     Dim strValue As String
  1940.     Dim strAxis As String
  1941.     Dim dblNextAxis
  1942.     'DIM iMeasureCount AS INTEGER
  1943.     Dim dblAverage As Double
  1944.     Dim sngAverage As Single
  1945.     Dim sLine As String
  1946.     Dim iX As Integer
  1947.     Dim iY As Integer
  1948.  
  1949.     Dim iCol As Integer
  1950.     Dim iRow As Integer
  1951.     Dim iCols As Integer
  1952.     Dim iRows As Integer
  1953.     Dim iColWidth As Integer
  1954.     Dim iColCount As Integer
  1955.     Dim iGroupCount As Integer
  1956.  
  1957.     ' SET UP SCREEN
  1958.     Screen _NewImage(1280, 1024, 32): _ScreenMove 0, 0
  1959.  
  1960.     ' INITIALIZE
  1961.     iDigits = 4 ' 11
  1962.     iColCount = 3
  1963.     iCols = _Width(0) \ _FontWidth
  1964.     iRows = _Height(0) \ _FontHeight
  1965.     iColWidth = iCols \ iColCount
  1966.  
  1967.     ' COUNT # OF JOYSTICKS
  1968.     ' D= _DEVICES ' MUST be read in order for other 2 device functions to work!
  1969.     iDeviceCount = _Devices ' Find the number of devices on someone's system
  1970.     If iDeviceCount < 3 Then
  1971.         Cls
  1972.         Print "NO JOYSTICKS FOUND, EXITING..."
  1973.         Input "PRESS <ENTER>"; in$
  1974.         Exit Sub
  1975.     End If
  1976.  
  1977.     ' BASE # OF PLAYERS ON HOW MANY CONTROLLERS FOUND
  1978.     iNumControllers = iDeviceCount - 2 ' TODO: find out the right way to count joysticks
  1979.     If iNumControllers > cMaxControllers Then
  1980.         iNumControllers = cMaxControllers
  1981.     End If
  1982.  
  1983.     ' INITIALIZE PLAYER COORDINATES AND SCREEN CHARACTERS
  1984.     iNextY = 1
  1985.     iNextX = -3
  1986.     iNextC = 64
  1987.     For iController = 1 To iNumControllers
  1988.         iNextX = iNextX + 4
  1989.         If iNextX > 80 Then
  1990.             iNextX = 1
  1991.             iNextY = iNextY + 4
  1992.         End If
  1993.         iNextC = iNextC + 1
  1994.         arrController(iController).buttonCount = cMaxButtons
  1995.         arrController(iController).axisCount = cMaxAxis
  1996.  
  1997.         For iLoop = 1 To cMaxButtons
  1998.             arrButtonNew(iController, iLoop) = TRUE
  1999.         Next iLoop
  2000.         For iLoop = 1 To cMaxAxis
  2001.             arrAxisNew(iController, iLoop) = TRUE
  2002.             arrAxisAvg(iController, iLoop) = 0
  2003.         Next iLoop
  2004.     Next iController
  2005.  
  2006.     ' CLEAR THE SCREEN
  2007.     'iMeasureCount = 0
  2008.     Do
  2009.         For iController = 1 To iNumControllers
  2010.             iDevice = iController + 2
  2011.  
  2012.             While _DeviceInput(iDevice) ' clear and update the device buffer
  2013.                 ''IF _DEVICEINPUT = 3 THEN ' this says we only care about joystick input values
  2014.  
  2015.                 ' check all the buttons
  2016.                 For iLoop = 1 To _LastButton(iDevice)
  2017.                     If (iLoop > cMaxButtons) Then
  2018.                         Exit For
  2019.                     End If
  2020.                     arrController(iController).buttonCount = iLoop
  2021.  
  2022.                     ' update button array to indicate if a button is up or down currently.
  2023.                     If _ButtonChange(iLoop) Then
  2024.                         '' _BUTTON(number) returns -1 when a button is pressed and 0 when released.
  2025.                         ''arrButton(iLoop) = NOT arrButton(iLoop)
  2026.                         arrButton(iController, iLoop) = _Button(iLoop)
  2027.                     End If
  2028.  
  2029.                     '' SAVE MINIMUM VALUE
  2030.                     'if arrButton(iController, iLoop) < arrButtonMin(iController, iLoop) then
  2031.                     '    arrButtonMin(iController, iLoop) = arrButton(iController, iLoop)
  2032.                     '
  2033.                     '    ' INITIALIZE THE MAX TO THE MINIMUM VALUE
  2034.                     '    IF arrButtonNew(iController, iLoop) = TRUE THEN
  2035.                     '        arrButtonMax(iController, iLoop) = arrButtonMin(iController, iLoop)
  2036.                     '        arrButtonNew(iController, iLoop) = FALSE
  2037.                     '    END IF
  2038.                     'end if
  2039.                     '
  2040.                     '' SAVE MAXIMUM VALUE
  2041.                     'if arrButton(iController, iLoop) > arrButtonMax(iController, iLoop) then
  2042.                     '    arrButtonMax(iController, iLoop) = arrButton(iController, iLoop)
  2043.                     'end if
  2044.  
  2045.                 Next iLoop
  2046.  
  2047.                 For iLoop = 1 To _LastAxis(iDevice) ' this loop checks all my axis
  2048.                     If (iLoop > cMaxAxis) Then
  2049.                         Exit For
  2050.                     End If
  2051.                     arrController(iController).axisCount = iLoop
  2052.  
  2053.                     ' I like to give a little "jiggle" resistance to my controls, as I have an old joystick
  2054.                     ' which is prone to always give minute values and never really center on true 0.
  2055.                     ' A value of 1 means my axis is pushed fully in one direction.
  2056.                     ' A value greater than 0.1 means it's been partially pushed in a direction (such as at a 45 degree diagional angle).
  2057.                     ' A value of less than 0.1 means we count it as being centered. (As if it was 0.)
  2058.                     'IF ABS(_AXIS(iLoop)) <= 1 AND ABS(_AXIS(iLoop)) >= .1 THEN
  2059.  
  2060.                     dblNextAxis = _Axis(iLoop)
  2061.                     dblNextAxis = RoundUpDouble#(dblNextAxis, 3)
  2062.                     'IF ABS(dblNextAxis) <= 1 AND ABS(dblNextAxis) >= .01 THEN
  2063.                     If Abs(dblNextAxis) <= 1 And Abs(dblNextAxis) >= .001 Then
  2064.                         arrAxis(iController, iLoop) = dblNextAxis
  2065.                     Else
  2066.                         arrAxis(iController, iLoop) = 0
  2067.                     End If
  2068.  
  2069.                     '' SAVE MINIMUM VALUE
  2070.                     'if arrAxis(iController, iLoop) < arrAxisMin(iController, iLoop) then
  2071.                     '    arrAxisMin(iController, iLoop) = arrAxis(iController, iLoop)
  2072.                     '
  2073.                     '    ' INITIALIZE THE MAX TO THE MINIMUM VALUE
  2074.                     '    IF arrAxisNew(iController, iLoop) = TRUE THEN
  2075.                     '        arrAxisMax(iController, iLoop) = arrAxisMin(iController, iLoop)
  2076.                     '        arrAxisNew(iController, iLoop) = FALSE
  2077.                     '    END IF
  2078.                     'end if
  2079.                     '
  2080.                     '' SAVE MAXIMUM VALUE
  2081.                     'if arrAxis(iController, iLoop) > arrAxisMax(iController, iLoop) then
  2082.                     '    arrAxisMax(iController, iLoop) = arrAxis(iController, iLoop)
  2083.                     'end if
  2084.                     '
  2085.                     '' ADD CURRENT VALUE TO AVERAGE SUM
  2086.                     'arrAxisAvg(iController, iLoop) = arrAxisAvg(iController, iLoop) + arrAxis(iController, iLoop)
  2087.  
  2088.                 Next iLoop
  2089.             Wend ' clear and update the device buffer
  2090.  
  2091.         Next iController
  2092.  
  2093.         'PRINT "*** iNumControllers=" + cstr$(iNumControllers) + " ***"
  2094.         'iMeasureCount = iMeasureCount + 1
  2095.         'if iMeasureCount = 10 then
  2096.         'iMeasureCount = 0
  2097.  
  2098.         ' And below here is just the simple display routine which displays our values.
  2099.         ' If this was for a game, I'd choose something like arrAxis(1) = -1 for a left arrow style input,
  2100.         ' arrAxis(1) = 1 for a right arrow style input, rather than just using _KEYHIT or INKEY$.
  2101.  
  2102.         InitColumns iColCount
  2103.         m_StartRow = 6
  2104.         m_EndRow = iRows - 2
  2105.         'm_StartCol
  2106.         'm_EndCol
  2107.  
  2108.         Cls
  2109.         PrintString 1, 1, "Game controller test program."
  2110.         PrintString 1, 2, "This program is free to use and distribute per GNU GPLv3 license."
  2111.         PrintString 1, 3, "Tests up to 4 controllers with 2 axes / 2 buttons each."
  2112.         PrintString 1, 4, "Plug in controllers and move them & press buttons."
  2113.         PrintString 1, 5, "-------------------------------------------------------------------------------"
  2114.  
  2115.         iGroupCount = 0
  2116.  
  2117.         For iController = 1 To iNumControllers
  2118.             For iLoop = 1 To arrController(iController).axisCount ' A loop for each axis
  2119.                 strAxis = Right$("  " + cstr$(iLoop), 2)
  2120.  
  2121.                 sLine = ""
  2122.  
  2123.                 ' display their status to the screen
  2124.                 sLine = sLine + "Player " + cstr$(iController)
  2125.  
  2126.                 strValue = FormatNumber$(arrAxis(iController, iLoop), iDigits)
  2127.                 sLine = sLine + ",   Axis #" + strAxis + " = " + strValue
  2128.  
  2129.                 'strValue = FormatNumber$(arrAxisMin(iController, iLoop), iDigits)
  2130.                 'sLine = sLine + ", Min=" + strValue
  2131.                 '
  2132.                 'strValue = FormatNumber$(arrAxisMax(iController, iLoop), iDigits)
  2133.                 'sLine = sLine + ", Max=" + strValue
  2134.                 '
  2135.                 '' COMPUTE AVERAGE
  2136.                 'dblAverage = arrAxisAvg(iController, iLoop) / 10
  2137.                 'dblAverage = RoundUpDouble# (dblAverage, 3)
  2138.                 'strValue = FormatNumber$(dblAverage, iDigits)
  2139.                 'sLine = sLine + ", Avg=" + strValue
  2140.                 '
  2141.                 '' CLEAR THE AVERAGE
  2142.                 'arrAxisAvg(iController, iLoop) = 0
  2143.  
  2144.                 PrintColumn sLine
  2145.             Next iLoop
  2146.             For iLoop = 1 To arrController(iController).buttonCount ' A loop for each button
  2147.                 strAxis = Right$("  " + cstr$(iLoop), 2)
  2148.  
  2149.                 sLine = ""
  2150.  
  2151.                 ' display their status to the screen
  2152.                 sLine = sLine + "Player " + cstr$(iController)
  2153.  
  2154.                 strValue = FormatNumber$(arrButton(iController, iLoop), iDigits)
  2155.                 sLine = sLine + ", Button #" + strAxis + " = " + strValue
  2156.  
  2157.                 'strValue = FormatNumber$(arrButtonMin(iController, iLoop), iDigits)
  2158.                 'sLine = sLine + ", Min=" + strValue
  2159.                 '
  2160.                 'strValue = FormatNumber$(arrButtonMax(iController, iLoop), iDigits)
  2161.                 'sLine = sLine + ", Max=" + strValue
  2162.  
  2163.                 PrintColumn sLine
  2164.             Next iLoop
  2165.  
  2166.             iGroupCount = iGroupCount + 1
  2167.             If iGroupCount = 2 Then
  2168.                 ColumnBreak
  2169.                 iGroupCount = 0
  2170.             End If
  2171.  
  2172.         Next iController
  2173.  
  2174.         PrintString 1, iRows - 1, "-------------------------------------------------------------------------------"
  2175.         PrintString 1, iRows - 0, "PRESS <ESC> TO EXIT"
  2176.  
  2177.         'end if
  2178.  
  2179.         _Limit 30
  2180.     Loop Until _KeyHit = 27 ' ESCAPE to quit
  2181.  
  2182.     ' RETURN TO TEXT SCREEN
  2183.     Screen 0
  2184. End Sub ' TestJoysticks1
  2185.  
  2186. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2187. ' BEGIN FILE FUNCTIONS
  2188. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2189.  
  2190. ' /////////////////////////////////////////////////////////////////////////////
  2191. ' File format is comma-delimited
  2192. ' containing controller info for one action per line
  2193. ' where each line contains the following in this order:
  2194.  
  2195. ' TAB ORDER   INFO             TYPE      DESCRIPTION
  2196. ' 1           {player #}       Integer   player # this mapping is for
  2197. ' 2           {which action}   Integer   which action this mapping is for (up/down/right/left/button 1/button 2, etc.)
  2198. ' 3           {device #}       Integer   number of the device this is mapped to
  2199. ' 4           {type}           Integer   type of input (one of: cInputKey, cInputButton, cInputAxis)
  2200. ' 5           {which}          Integer   if button the _BUTTON #, if axis the _AXIS #, if keyboard the _BUTTON #
  2201. ' 6           {value}          Integer   if axis, the value (-1 or 1), else can be ignored
  2202.  
  2203. ' These need to be declared globally and populated:
  2204. '     ReDim Shared m_arrControlMap(1 To 8, 1 To 8) As ControlInputType
  2205. '     Dim Shared m_ControlMapFileName$: m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  2206.  
  2207. ' If there is an error, returns error message,
  2208. ' else returns blank string.
  2209.  
  2210. Function SaveControllerMap$
  2211.     Dim RoutineName As String:: RoutineName = "SaveControllerMap$"
  2212.     Dim sResult As String: sResult = ""
  2213.     Dim sError As String: sError = ""
  2214.     Dim sFile As String
  2215.     Dim in$
  2216.     Dim iPlayer As Integer
  2217.     Dim iWhichInput As Integer
  2218.     Dim sLine As String
  2219.     Dim iCount As Long: iCount = 0
  2220.     'Dim iError As Long: iError = 0
  2221.     Dim sDelim As String: sDelim = "," ' CHR$(9)
  2222.  
  2223.     DebugPrint "--------------------------------------------------------------------------------"
  2224.     DebugPrint "Started " + RoutineName
  2225.     DebugPrint "--------------------------------------------------------------------------------"
  2226.  
  2227.     ' Get file name
  2228.     If Len(m_ControlMapFileName$) = 0 Then
  2229.         m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  2230.     End If
  2231.     sFile = Mid$(m_ControlMapFileName$, _InStrRev(m_ControlMapFileName$, "\") + 1)
  2232.  
  2233.     '_KeyClear
  2234.     'Cls
  2235.     'Print "SAVE CONTROLLER MAPPING:"
  2236.     'Print "Default file name is " + Chr$(34) + m_ControlMapFileName$ + Chr$(34) + "."
  2237.     'Input "Type save file name, or blank for default: ", in$
  2238.     'in$ = _Trim$(in$)
  2239.     'If Len(in$) > 0 Then
  2240.     '    m_ControlMapFileName$ = in$
  2241.     'End If
  2242.     'sFile = m_ProgramPath$ + m_ControlMapFileName$
  2243.  
  2244.     DebugPrint "m_ControlMapFileName$=" + Chr$(34) + m_ControlMapFileName$ + Chr$(34)
  2245.  
  2246.     ' Save mapping to file
  2247.     Open m_ControlMapFileName$ For Output As #1
  2248.  
  2249.     For iPlayer = LBound(m_arrControlMap, 1) To UBound(m_arrControlMap, 1)
  2250.         For iWhichInput = LBound(m_arrControlMap, 2) To UBound(m_arrControlMap, 2)
  2251.             sLine = ""
  2252.  
  2253.             sLine = sLine + _Trim$(Str$(iPlayer))
  2254.             sLine = sLine + sDelim
  2255.  
  2256.             sLine = sLine + _Trim$(Str$(iWhichInput))
  2257.             sLine = sLine + sDelim
  2258.  
  2259.             sLine = sLine + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).device))
  2260.             sLine = sLine + sDelim
  2261.  
  2262.             sLine = sLine + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).typ))
  2263.             sLine = sLine + sDelim
  2264.  
  2265.             sLine = sLine + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).code))
  2266.             sLine = sLine + sDelim
  2267.  
  2268.             sLine = sLine + _Trim$(Str$(m_arrControlMap(iPlayer, iWhichInput).value))
  2269.  
  2270.             Print #1, sLine
  2271.             iCount = iCount + 1
  2272.         Next iWhichInput
  2273.     Next iPlayer
  2274.  
  2275.     Close #1
  2276.  
  2277.     DebugPrint "Wrote   " + _Trim$(Str$(iCount)) + " lines."
  2278.     'Print "Skipped " + _Trim$(Str$(iError)) + " lines."
  2279.     DebugPrint ""
  2280.     'Input "PRESS <ENTER> TO CONTINUE", in$
  2281.  
  2282.     If Len(sError) = 0 Then
  2283.         sResult = "Saved mapping file " + Chr$(34) + sFile + Chr$(34) + "."
  2284.     Else
  2285.         sResult = "ERRORS: " + sError
  2286.     End If
  2287.  
  2288.     SaveControllerMap$ = sResult
  2289. End Function ' SaveControllerMap$
  2290.  
  2291. ' /////////////////////////////////////////////////////////////////////////////
  2292.  
  2293. Function LoadControllerMap$
  2294.     Dim RoutineName As String:: RoutineName = "LoadControllerMap$"
  2295.     Dim sResult As String: sResult = ""
  2296.     Dim sError As String: sError = ""
  2297.     Dim sNextErr As String
  2298.  
  2299.     Dim sFile As String
  2300.     Dim sText As String
  2301.     Dim iTotal As Long: iTotal = 0
  2302.  
  2303.     Dim iRead As Long: iRead = 0
  2304.     Dim iValid As Long: iValid = 0
  2305.     Dim iBad As Long: iBad = 0
  2306.     Dim iBlank As Long: iBlank = 0
  2307.  
  2308.     Dim sLine As String
  2309.     ReDim arrNextLine(-1) As String
  2310.     Dim iNumValues As Integer
  2311.     Dim iAdjust As Integer
  2312.  
  2313.     Dim iPlayer As Integer
  2314.     Dim iWhichInput As Integer
  2315.     Dim iDevice As Integer
  2316.     Dim iType As Integer
  2317.     Dim iCode As Integer
  2318.     Dim iValue As Integer
  2319.  
  2320.     'Dim sDebugLine As String
  2321.  
  2322.     DebugPrint "--------------------------------------------------------------------------------"
  2323.     DebugPrint "Started " + RoutineName
  2324.     DebugPrint "--------------------------------------------------------------------------------"
  2325.  
  2326.     ' Get file name
  2327.     If Len(sError) = 0 Then
  2328.         If Len(m_ControlMapFileName$) = 0 Then
  2329.             m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  2330.         End If
  2331.         sFile = Mid$(m_ControlMapFileName$, _InStrRev(m_ControlMapFileName$, "\") + 1)
  2332.     End If
  2333.  
  2334.     '' Get file name
  2335.     'If Len(sError) = 0 Then
  2336.     '    Cls
  2337.     '    If Len(m_ControlMapFileName$) = 0 Then
  2338.     '        m_ControlMapFileName$ = Left$(m_ProgramName$, _InStrRev(m_ProgramName$, ".")) + "map.txt"
  2339.     '    End If
  2340.     '    Print "LOAD CONTROLLER MAPPING:"
  2341.     '    Print "Default file name is " + Chr$(34) + m_ControlMapFileName$ + Chr$(34) + "."
  2342.     '    Input "Type name of file to open, or blank for default: ", in$
  2343.     '    in$ = _Trim$(in$)
  2344.     '    If Len(in$) > 0 Then
  2345.     '        m_ControlMapFileName$ = in$
  2346.     '    End If
  2347.     '    sFile = m_ProgramPath$ + m_ControlMapFileName$
  2348.     'End If
  2349.  
  2350.     ' Make sure file exists
  2351.     If Len(sError) = 0 Then
  2352.         If _FileExists(m_ControlMapFileName$) = FALSE Then
  2353.             sError = "File not found: " + Chr$(34) + m_ControlMapFileName$ + Chr$(34)
  2354.         Else
  2355.             DebugPrint "Found file: " + Chr$(34) + m_ControlMapFileName$ + Chr$(34)
  2356.         End If
  2357.     End If
  2358.  
  2359.     ' Read data from file
  2360.     If Len(sError) = 0 Then
  2361.         DebugPrint "OPEN m_ControlMapFileName$ FOR BINARY AS #1"
  2362.  
  2363.         Open m_ControlMapFileName$ For Binary As #1
  2364.         sText = Space$(LOF(1))
  2365.         Get #1, , sText
  2366.         Close #1
  2367.         iTotal = Len(sText) - Len(Replace$(sText, Chr$(13), ""))
  2368.         sText = ""
  2369.  
  2370.         Open m_ControlMapFileName$ For Input As #1
  2371.         While Not EOF(1)
  2372.             'INPUT #1, sLine
  2373.             Line Input #1, sLine ' read entire text file line
  2374.  
  2375.             iRead = iRead + 1
  2376.             DebugPrint "Parsing line " + _Trim$(Str$(iRead)) + _
  2377.                 " of " + _Trim$(Str$(iTotal))
  2378.  
  2379.             sLine = Replace$(sLine, " ", "") ' Remove spaces
  2380.             sLine = Replace$(sLine, Chr$(9), "") ' Remove tabs
  2381.             sLine = Replace$(sLine, Chr$(10), "") ' Remove line breaks
  2382.             sLine = Replace$(sLine, Chr$(13), "") ' Remove carriage returns
  2383.             DebugPrint "    Trimmed=" + Chr$(34) + sLine + Chr$(34)
  2384.  
  2385.             If Len(sLine) > 0 Then
  2386.                 split sLine, ",", arrNextLine()
  2387.                 DebugPrint "split into arrNextLine()"
  2388.                 DebugPrint "    lbound =" + _Trim$(Str$(LBound(arrNextLine))) '+ CHR$(10)
  2389.                 DebugPrint "    ubound =" + _Trim$(Str$(UBound(arrNextLine))) '+ CHR$(10)
  2390.  
  2391.                 iNumValues = (UBound(arrNextLine) - LBound(arrNextLine)) + 1
  2392.                 If iNumValues > 5 Then
  2393.                     iAdjust = -1 '- lbound(arrNextLine)
  2394.  
  2395.                     If Len(sNextErr) = 0 Then
  2396.                         If IsNum%(arrNextLine(1 + iAdjust)) = TRUE Then
  2397.                             iPlayer = Val(arrNextLine(1 + iAdjust))
  2398.                         Else
  2399.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 1: not a number"
  2400.                         End If
  2401.                     End If
  2402.  
  2403.                     If Len(sNextErr) = 0 Then
  2404.                         If IsNum%(arrNextLine(2 + iAdjust)) = TRUE Then
  2405.                             iWhichInput = Val(arrNextLine(2 + iAdjust))
  2406.                         Else
  2407.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 2: not a number"
  2408.                         End If
  2409.                     End If
  2410.  
  2411.                     If Len(sNextErr) = 0 Then
  2412.                         If IsNum%(arrNextLine(3 + iAdjust)) = TRUE Then
  2413.                             iDevice = Val(arrNextLine(3 + iAdjust))
  2414.                         Else
  2415.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 3: not a number"
  2416.                         End If
  2417.                     End If
  2418.  
  2419.                     If Len(sNextErr) = 0 Then
  2420.                         If IsNum%(arrNextLine(4 + iAdjust)) = TRUE Then
  2421.                             iType = Val(arrNextLine(4 + iAdjust))
  2422.                         Else
  2423.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 4: not a number"
  2424.                         End If
  2425.                     End If
  2426.  
  2427.                     If Len(sNextErr) = 0 Then
  2428.                         If IsNum%(arrNextLine(5 + iAdjust)) = TRUE Then
  2429.                             iCode = Val(arrNextLine(5 + iAdjust))
  2430.                         Else
  2431.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 5: not a number"
  2432.                         End If
  2433.                     End If
  2434.  
  2435.                     If Len(sNextErr) = 0 Then
  2436.                         If IsNum%(arrNextLine(6 + iAdjust)) = TRUE Then
  2437.                             iValue = Val(arrNextLine(6 + iAdjust))
  2438.                         Else
  2439.                             sNextErr = "Error on line " + cstr$(iRead) + ", value 6: not a number"
  2440.                         End If
  2441.                     End If
  2442.  
  2443.                     ' validate iPlayer
  2444.                     If Len(sNextErr) = 0 Then
  2445.                         If iPlayer < LBound(m_arrControlMap, 1) Then
  2446.                             sNextErr = "Player value " + _Trim$(Str$(iPlayer)) + _
  2447.                                 " is outside lbound(m_arrControlMap, 1) " + _
  2448.                                 " which is " + _Trim$(Str$(lbound(m_arrControlMap, 1))) + "."
  2449.                         ElseIf iPlayer > UBound(m_arrControlMap, 1) Then
  2450.                             sNextErr = "Player value " + _Trim$(Str$(iPlayer)) + _
  2451.                                 " is outside ubound(m_arrControlMap, 1) " + _
  2452.                                 " which is " + _Trim$(Str$(ubound(m_arrControlMap, 1))) + "."
  2453.                         End If
  2454.                     End If
  2455.  
  2456.                     ' validate iWhichInput
  2457.                     If Len(sNextErr) = 0 Then
  2458.                         If iWhichInput < LBound(m_arrControlMap, 2) Then
  2459.                             sNextErr = "WhichInput value " + _Trim$(Str$(iWhichInput)) + _
  2460.                                 " is outside lbound(m_arrControlMap, 2) " + _
  2461.                                 " which is " + _Trim$(Str$(lbound(m_arrControlMap, 2))) + "."
  2462.                         ElseIf iWhichInput > UBound(m_arrControlMap, 2) Then
  2463.                             sNextErr = "WhichInput value " + _Trim$(Str$(iWhichInput)) + _
  2464.                                 " is outside ubound(m_arrControlMap, 2) " + _
  2465.                                 " which is " + _Trim$(Str$(ubound(m_arrControlMap, 2))) + "."
  2466.                         End If
  2467.                     End If
  2468.  
  2469.                 Else
  2470.                     sNextErr = "Error on line " + cstr$(iRead) + ": detected " + cstr$(iNumValues) + " values, expected 6."
  2471.                 End If
  2472.  
  2473.                 If Len(sNextErr) = 0 Then
  2474.                     iValid = iValid + 1
  2475.                     m_arrControlMap(iPlayer, iWhichInput).device = iDevice
  2476.                     m_arrControlMap(iPlayer, iWhichInput).typ = iType
  2477.                     m_arrControlMap(iPlayer, iWhichInput).code = iCode
  2478.                     m_arrControlMap(iPlayer, iWhichInput).value = iValue
  2479.                 Else
  2480.                     iBad = iBad + 1
  2481.                     DebugPrint sNextErr
  2482.                 End If
  2483.             Else
  2484.                 DebugPrint "    Line is blank: skipped"
  2485.                 iBlank = iBlank + 1
  2486.             End If ' LEN(sLine) > 0
  2487.  
  2488.         Wend
  2489.         Close #1
  2490.     End If
  2491.  
  2492.     DebugPrint ""
  2493.     DebugPrint "Lines read: " + _Trim$(Str$(iRead))
  2494.     DebugPrint "Valid     : " + _Trim$(Str$(iValid))
  2495.     DebugPrint "Invalid   : " + _Trim$(Str$(iErrors))
  2496.     DebugPrint "Blank     : " + _Trim$(Str$(iBlank))
  2497.     DebugPrint ""
  2498.     'Input "PRESS <ENTER> TO CONTINUE", in$
  2499.  
  2500.     If Len(sError) = 0 Then
  2501.         sResult = "Loaded mapping file " + Chr$(34) + sFile + Chr$(34) + "."
  2502.     Else
  2503.         sResult = "ERRORS: " + sError
  2504.     End If
  2505.  
  2506.     LoadControllerMap$ = sResult
  2507. End Function ' LoadControllerMap$
  2508.  
  2509. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2510. ' END FILE FUNCTIONS
  2511. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2512.  
  2513. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2514. ' BEGIN GRAPHIC PRINTING ROUTINES
  2515. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2516.  
  2517. ' /////////////////////////////////////////////////////////////////////////////
  2518. ' Eliminates the math.
  2519.  
  2520. ' Text resolution:
  2521. '  648 x  480:  80 x 30
  2522. '  720 x  480:  90 x 30
  2523. '  800 x  600: 100 x 37
  2524. ' 1024 x  768: 128 x 48
  2525. ' 1280 x 1024: 160 x 64
  2526. ' 1920 x 1080: 240 x 67
  2527. ' 2048 x 1152: 256 x 72 (truncated after 70 rows, 255 columns)
  2528. ' 3840 x 2160: 480 x135 (truncated after 133 rows, 479 columns)
  2529.  
  2530. Sub PrintString (iCol As Integer, iRow As Integer, MyString As String)
  2531.     Dim iCols As Integer
  2532.     Dim iRows As Integer
  2533.     Dim iX As Integer
  2534.     Dim iY As Integer
  2535.     iCols = _Width(0) \ _FontWidth
  2536.     iRows = _Height(0) \ _FontHeight
  2537.     iX = _FontWidth * (iCol - 1)
  2538.     iY = _FontHeight * (iRow - 1)
  2539.     _PrintString (iX, iY), MyString
  2540. End Sub ' PrintString
  2541.  
  2542. ' /////////////////////////////////////////////////////////////////////////////
  2543. ' A way to automatically print to columns.
  2544.  
  2545. ' Depends on the following shared variables:
  2546. '     Dim Shared m_NumColumns As Integer : m_NumColumns = 1
  2547. '     Dim Shared m_PrintRow As Integer : m_PrintRow = 0
  2548. '     Dim Shared m_PrintCol As Integer : m_PrintCol = 0
  2549. '     Dim Shared m_StartRow As Integer : m_StartRow = 0
  2550. '     Dim Shared m_EndRow As Integer : m_EndRow = 0
  2551. '     Dim Shared m_StartCol As Integer : m_StartCol = 0
  2552. '     Dim Shared m_EndCol As Integer : m_EndCol = 0
  2553.  
  2554. ' InitColumns 2
  2555. ' m_PrintRow = 5
  2556. ' m_PrintCol = 2
  2557. ' PrintColumn "Col 2, Row 5"
  2558. ' PrintColumn "m_NumColumns=" + cstr$(m_NumColumns)
  2559.  
  2560. Sub PrintColumn (MyString As String)
  2561.     Dim iCols As Integer
  2562.     Dim iRows As Integer
  2563.     Dim iX As Integer
  2564.     Dim iY As Integer
  2565.  
  2566.     ReDim arrLines(-1) As String
  2567.     Dim iRow As Integer
  2568.     Dim iCol As Integer
  2569.     Dim sChar As String
  2570.     Dim sLine As String
  2571.     Dim iColWidth As Integer
  2572.  
  2573.     iCols = _Width(0) \ _FontWidth
  2574.     iRows = _Height(0) \ _FontHeight
  2575.  
  2576.     If m_NumColumns < 1 Or m_NumColumns > iCols Then
  2577.         m_NumColumns = 1
  2578.     End If
  2579.  
  2580.     If m_StartRow < 1 Or m_StartRow > iRows Then
  2581.         m_StartRow = 1
  2582.     End If
  2583.     If m_EndRow < m_StartRow Or m_EndRow > iRows Then
  2584.         m_EndRow = iRows
  2585.     End If
  2586.     If m_StartCol < 1 Or m_StartCol > m_NumColumns Then
  2587.         m_StartCol = 1
  2588.     End If
  2589.     If m_EndCol < m_StartCol Or m_EndCol > m_NumColumns Then
  2590.         m_EndCol = m_NumColumns
  2591.     End If
  2592.  
  2593.     If m_PrintRow < m_StartRow Then
  2594.         m_PrintRow = m_StartRow
  2595.     End If
  2596.     If m_PrintCol < m_StartCol Then
  2597.         m_PrintCol = m_StartCol
  2598.     End If
  2599.  
  2600.     iColWidth = iCols \ m_NumColumns
  2601.  
  2602.     If m_PrintRow <= m_EndRow Then
  2603.         If m_PrintCol <= m_EndCol Then
  2604.             split MyString, Chr$(13), arrLines()
  2605.             For iRow = 0 To UBound(arrlines)
  2606.                 sLine = Left$(arrLines(iRow), iColWidth)
  2607.                 'TODO: wrap remaining text
  2608.                 iX = _FontWidth * ((m_PrintCol - 1) * iColWidth)
  2609.                 iY = _FontHeight * (m_PrintRow - 1)
  2610.                 _PrintString (iX, iY), sLine
  2611.  
  2612.                 m_PrintRow = m_PrintRow + 1
  2613.                 If m_PrintRow > m_EndRow Then
  2614.                     m_PrintRow = m_StartRow
  2615.                     m_PrintCol = m_PrintCol + 1
  2616.                     If m_PrintCol > m_NumColumns Then
  2617.                         'TODO: options for when we reach the bottom of the last column (stop printing, wrap around)
  2618.                         m_PrintCol = 1
  2619.                     End If
  2620.                 End If
  2621.             Next iRow
  2622.         End If
  2623.     End If
  2624. End Sub ' PrintColumn
  2625.  
  2626. ' /////////////////////////////////////////////////////////////////////////////
  2627. ' A way to automatically print to columns.
  2628.  
  2629. Sub ColumnBreak ()
  2630.     m_PrintRow = m_StartRow
  2631.     m_PrintCol = m_PrintCol + 1
  2632.     If m_PrintCol > m_NumColumns Then
  2633.         'TODO: options for when we go past the last column (stop printing, wrap around)
  2634.     End If
  2635. End Sub ' ColumnBreak
  2636.  
  2637. ' /////////////////////////////////////////////////////////////////////////////
  2638. ' A way to automatically print to columns.
  2639.  
  2640. Sub InitColumns (iNumColumns As Integer)
  2641.     Dim iCols As Integer
  2642.     Dim iRows As Integer
  2643.     iCols = _Width(0) \ _FontWidth
  2644.     iRows = _Height(0) \ _FontHeight
  2645.     If iNumColumns < 1 Or iNumColumns > iCols Then
  2646.         m_NumColumns = 1
  2647.     Else
  2648.         m_NumColumns = iNumColumns
  2649.     End If
  2650.  
  2651.     If m_StartRow < 1 Or m_StartRow > iRows Then
  2652.         m_StartRow = 1
  2653.     End If
  2654.     If m_EndRow < m_StartRow Or m_EndRow > iRows Then
  2655.         m_EndRow = iRows
  2656.     End If
  2657.     If m_StartCol < 1 Or m_StartCol > m_NumColumns Then
  2658.         m_StartCol = 1
  2659.     End If
  2660.     If m_EndCol < m_StartCol Or m_EndCol > m_NumColumns Then
  2661.         m_EndCol = m_NumColumns
  2662.     End If
  2663.  
  2664.     m_PrintRow = 1
  2665.     m_PrintCol = 1
  2666. End Sub ' InitColumns
  2667.  
  2668. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2669. ' END GRAPHIC PRINTING ROUTINES
  2670. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2671.  
  2672. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2673. ' BEGIN DEBUGGING ROUTINES #DEBUGGING
  2674. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2675. Sub DebugPrint (s$)
  2676.     If m_bTesting = TRUE Then
  2677.         _Echo s$
  2678.         'ReDim arrLines$(0)
  2679.         'dim delim$ : delim$ = Chr$(13)
  2680.         'split MyString, delim$, arrLines$()
  2681.     End If
  2682. End Sub ' DebugPrint
  2683.  
  2684. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2685. ' END DEBUGGING ROUTINES @DEBUGGING
  2686. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2687.  
  2688. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2689. ' BEGIN GENERAL PURPOSE ROUTINES #GEN
  2690. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2691.  
  2692. ' /////////////////////////////////////////////////////////////////////////////
  2693. ' Convert a value to string and trim it (because normal Str$ adds spaces)
  2694.  
  2695. Function cstr$ (myValue)
  2696.     'cstr$ = LTRIM$(RTRIM$(STR$(myValue)))
  2697.     cstr$ = _Trim$(Str$(myValue))
  2698. End Function ' cstr$
  2699.  
  2700. ' /////////////////////////////////////////////////////////////////////////////
  2701. ' Convert a Long value to string and trim it (because normal Str$ adds spaces)
  2702.  
  2703. Function cstrl$ (myValue As Long)
  2704.     cstrl$ = _Trim$(Str$(myValue))
  2705. End Function ' cstrl$
  2706.  
  2707. ' /////////////////////////////////////////////////////////////////////////////
  2708. ' Convert a Single value to string and trim it (because normal Str$ adds spaces)
  2709.  
  2710. Function cstrs$ (myValue As Single)
  2711.     ''cstr$ = LTRIM$(RTRIM$(STR$(myValue)))
  2712.     cstrs$ = _Trim$(Str$(myValue))
  2713. End Function ' cstrs$
  2714.  
  2715. ' /////////////////////////////////////////////////////////////////////////////
  2716. ' Convert an unsigned Long value to string and trim it (because normal Str$ adds spaces)
  2717.  
  2718. Function cstrul$ (myValue As _Unsigned Long)
  2719.     cstrul$ = _Trim$(Str$(myValue))
  2720. End Function ' cstrul$
  2721.  
  2722. ' /////////////////////////////////////////////////////////////////////////////
  2723. ' Scientific notation - QB64 Wiki
  2724. ' https://www.qb64.org/wiki/Scientific_notation
  2725.  
  2726. ' Example: A string function that displays extremely small or large exponential decimal values.
  2727.  
  2728. Function DblToStr$ (n#)
  2729.     value$ = UCase$(LTrim$(Str$(n#)))
  2730.     Xpos% = InStr(value$, "D") + InStr(value$, "E") 'only D or E can be present
  2731.     If Xpos% Then
  2732.         expo% = Val(Mid$(value$, Xpos% + 1))
  2733.         If Val(value$) < 0 Then
  2734.             sign$ = "-": valu$ = Mid$(value$, 2, Xpos% - 2)
  2735.         Else valu$ = Mid$(value$, 1, Xpos% - 1)
  2736.         End If
  2737.         dot% = InStr(valu$, "."): L% = Len(valu$)
  2738.         If expo% > 0 Then add$ = String$(expo% - (L% - dot%), "0")
  2739.         If expo% < 0 Then min$ = String$(Abs(expo%) - (dot% - 1), "0"): DP$ = "."
  2740.         For n = 1 To L%
  2741.             If Mid$(valu$, n, 1) <> "." Then num$ = num$ + Mid$(valu$, n, 1)
  2742.         Next
  2743.     Else DblToStr$ = value$: Exit Function
  2744.     End If
  2745.     DblToStr$ = _Trim$(sign$ + DP$ + min$ + num$ + add$)
  2746. End Function ' DblToStr$
  2747.  
  2748. ' /////////////////////////////////////////////////////////////////////////////
  2749.  
  2750. Function FormatNumber$ (myValue, iDigits As Integer)
  2751.     Dim strValue As String
  2752.     strValue = DblToStr$(myValue) + String$(iDigits, " ")
  2753.     If myValue < 1 Then
  2754.         If myValue < 0 Then
  2755.             strValue = Replace$(strValue, "-.", "-0.")
  2756.         ElseIf myValue > 0 Then
  2757.             strValue = "0" + strValue
  2758.         End If
  2759.     End If
  2760.     FormatNumber$ = Left$(strValue, iDigits)
  2761. End Function ' FormatNumber$
  2762.  
  2763. ' /////////////////////////////////////////////////////////////////////////////
  2764. ' From: Bitwise Manipulations By Steven Roman
  2765. ' http://www.romanpress.com/Articles/Bitwise_R/Bitwise.htm
  2766.  
  2767. ' Returns the 8-bit binary representation
  2768. ' of an integer iInput where 0 <= iInput <= 255
  2769.  
  2770. Function GetBinary$ (iInput1 As Integer)
  2771.     Dim sResult As String
  2772.     Dim iLoop As Integer
  2773.     Dim iInput As Integer: iInput = iInput1
  2774.  
  2775.     sResult = ""
  2776.  
  2777.     If iInput >= 0 And iInput <= 255 Then
  2778.         For iLoop = 1 To 8
  2779.             sResult = LTrim$(RTrim$(Str$(iInput Mod 2))) + sResult
  2780.             iInput = iInput \ 2
  2781.             'If iLoop = 4 Then sResult = " " + sResult
  2782.         Next iLoop
  2783.     End If
  2784.  
  2785.     GetBinary$ = sResult
  2786. End Function ' GetBinary$
  2787.  
  2788. ' /////////////////////////////////////////////////////////////////////////////
  2789. ' wonderfully inefficient way to read if a bit is set
  2790. ' ival = GetBit256%(int we are comparing, int containing the bits we want to read)
  2791.  
  2792. ' See also: GetBit256%, SetBit256%
  2793.  
  2794. Function GetBit256% (iNum1 As Integer, iBit1 As Integer)
  2795.     Dim iResult As Integer
  2796.     Dim sNum As String
  2797.     Dim sBit As String
  2798.     Dim iLoop As Integer
  2799.     Dim bContinue As Integer
  2800.     'DIM iTemp AS INTEGER
  2801.     Dim iNum As Integer: iNum = iNum1
  2802.     Dim iBit As Integer: iBit = iBit1
  2803.  
  2804.     iResult = FALSE
  2805.     bContinue = TRUE
  2806.  
  2807.     If iNum < 256 And iBit <= 128 Then
  2808.         sNum = GetBinary$(iNum)
  2809.         sBit = GetBinary$(iBit)
  2810.         For iLoop = 1 To 8
  2811.             If Mid$(sBit, iLoop, 1) = "1" Then
  2812.                 'if any of the bits in iBit are false, return false
  2813.                 If Mid$(sNum, iLoop, 1) = "0" Then
  2814.                     iResult = FALSE
  2815.                     bContinue = FALSE
  2816.                     Exit For
  2817.                 End If
  2818.             End If
  2819.         Next iLoop
  2820.         If bContinue = TRUE Then
  2821.             iResult = TRUE
  2822.         End If
  2823.     End If
  2824.  
  2825.     GetBit256% = iResult
  2826. End Function ' GetBit256%
  2827.  
  2828. ' /////////////////////////////////////////////////////////////////////////////
  2829. ' From: Bitwise Manipulations By Steven Roman
  2830. ' http://www.romanpress.com/Articles/Bitwise_R/Bitwise.htm
  2831.  
  2832. ' Returns the integer that corresponds to a binary string of length 8
  2833.  
  2834. Function GetIntegerFromBinary% (sBinary1 As String)
  2835.     Dim iResult As Integer
  2836.     Dim iLoop As Integer
  2837.     Dim strBinary As String
  2838.     Dim sBinary As String: sBinary = sBinary1
  2839.  
  2840.     iResult = 0
  2841.     strBinary = Replace$(sBinary, " ", "") ' Remove any spaces
  2842.     For iLoop = 0 To Len(strBinary) - 1
  2843.         iResult = iResult + 2 ^ iLoop * Val(Mid$(strBinary, Len(strBinary) - iLoop, 1))
  2844.     Next iLoop
  2845.  
  2846.     GetIntegerFromBinary% = iResult
  2847. End Function ' GetIntegerFromBinary%
  2848.  
  2849. ' /////////////////////////////////////////////////////////////////////////////
  2850.  
  2851. Function IIF (Condition, IfTrue, IfFalse)
  2852.     If Condition Then IIF = IfTrue Else IIF = IfFalse
  2853.  
  2854. ' /////////////////////////////////////////////////////////////////////////////
  2855.  
  2856. Function IIFSTR$ (Condition, IfTrue$, IfFalse$)
  2857.     If Condition Then IIFSTR$ = IfTrue$ Else IIFSTR$ = IfFalse$
  2858.  
  2859. ' /////////////////////////////////////////////////////////////////////////////
  2860. ' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/
  2861.  
  2862. Function IsEven% (n)
  2863.     If n Mod 2 = 0 Then
  2864.         IsEven% = TRUE
  2865.     Else
  2866.         IsEven% = FALSE
  2867.     End If
  2868. End Function ' IsEven%
  2869.  
  2870. ' /////////////////////////////////////////////////////////////////////////////
  2871. ' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/
  2872.  
  2873. Function IsOdd% (n)
  2874.     If n Mod 2 = 1 Then
  2875.         IsOdd% = TRUE
  2876.     Else
  2877.         IsOdd% = FALSE
  2878.     End If
  2879. End Function ' IsOdd%
  2880.  
  2881. ' /////////////////////////////////////////////////////////////////////////////
  2882. ' By sMcNeill from https://www.qb64.org/forum/index.php?topic=896.0
  2883.  
  2884. Function IsNum% (text$)
  2885.     Dim a$
  2886.     Dim b$
  2887.     a$ = _Trim$(text$)
  2888.     b$ = _Trim$(Str$(Val(text$)))
  2889.     If a$ = b$ Then
  2890.         IsNum% = TRUE
  2891.     Else
  2892.         IsNum% = FALSE
  2893.     End If
  2894. End Function ' IsNum%
  2895.  
  2896. ' /////////////////////////////////////////////////////////////////////////////
  2897. ' Re: Does a Is Number function exist in QB64?
  2898. ' https://www.qb64.org/forum/index.php?topic=896.15
  2899.  
  2900. ' MWheatley
  2901. ' « Reply #18 on: January 01, 2019, 11:24:30 AM »
  2902.  
  2903. ' returns 1 if string is an integer, 0 if not
  2904. Function IsNumber (text$)
  2905.     Dim i As Integer
  2906.  
  2907.     IsNumber = 1
  2908.     For i = 1 To Len(text$)
  2909.         If Asc(Mid$(text$, i, 1)) < 45 Or Asc(Mid$(text$, i, 1)) >= 58 Then
  2910.             IsNumber = 0
  2911.             Exit For
  2912.         ElseIf Asc(Mid$(text$, i, 1)) = 47 Then
  2913.             IsNumber = 0
  2914.             Exit For
  2915.         End If
  2916.     Next i
  2917. End Function ' IsNumber
  2918.  
  2919. ' /////////////////////////////////////////////////////////////////////////////
  2920. ' Split and join strings
  2921. ' https://www.qb64.org/forum/index.php?topic=1073.0
  2922.  
  2923. 'Combine all elements of in$() into a single string with delimiter$ separating the elements.
  2924.  
  2925. Function join$ (in$(), delimiter$)
  2926.     Dim result$
  2927.     Dim i As Long
  2928.     result$ = in$(LBound(in$))
  2929.     For i = LBound(in$) + 1 To UBound(in$)
  2930.         result$ = result$ + delimiter$ + in$(i)
  2931.     Next i
  2932.     join$ = result$
  2933. End Function ' join$
  2934.  
  2935. ' /////////////////////////////////////////////////////////////////////////////
  2936. ' ABS was returning strange values with type LONG
  2937. ' so I created this which does not.
  2938.  
  2939. Function LongABS& (lngValue As Long)
  2940.     If Sgn(lngValue) = -1 Then
  2941.         LongABS& = 0 - lngValue
  2942.     Else
  2943.         LongABS& = lngValue
  2944.     End If
  2945. End Function ' LongABS&
  2946.  
  2947. ' /////////////////////////////////////////////////////////////////////////////
  2948. ' Writes sText to a debug file in the EXE folder.
  2949. ' Debug file is named the same thing as the program EXE name with ".txt" at the end.
  2950. ' For example the program "C:\QB64\MyProgram.BAS" running as
  2951. ' "C:\QB64\MyProgram.EXE" would have an output file "C:\QB64\MyProgram.EXE.txt".
  2952. ' If the file doesn't exist, it is created, otherwise it is appended to.
  2953.  
  2954. Sub DebugPrintFile (sText As String)
  2955.     Dim sFileName As String
  2956.     Dim sError As String
  2957.     Dim sOut As String
  2958.  
  2959.     sFileName = ProgramPath$ + ProgramName$ + ".txt"
  2960.     sError = ""
  2961.     If _FileExists(sFileName) = FALSE Then
  2962.         sOut = ""
  2963.         sOut = sOut + "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10)
  2964.         sOut = sOut + "PROGRAM : " + ProgramName$ + Chr$(13) + Chr$(10)
  2965.         sOut = sOut + "RUN DATE: " + CurrentDateTime$ + Chr$(13) + Chr$(10)
  2966.         sOut = sOut + "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + Chr$(13) + Chr$(10)
  2967.         sError = PrintFile$(sFileName, sOut, FALSE)
  2968.     End If
  2969.     If Len(sError) = 0 Then
  2970.         sError = PrintFile$(sFileName, sText, TRUE)
  2971.     End If
  2972.     If Len(sError) <> 0 Then
  2973.         Print CurrentDateTime$ + " DebugPrintFile FAILED: " + sError
  2974.     End If
  2975. End Sub ' DebugPrintFile
  2976.  
  2977. ' /////////////////////////////////////////////////////////////////////////////
  2978.  
  2979. Function IntPadRight$ (iValue As Integer, iWidth As Integer)
  2980.     IntPadRight$ = Left$(_Trim$(Str$(iValue)) + String$(iWidth, " "), iWidth)
  2981. End Function ' IntPadRight$
  2982.  
  2983. ' /////////////////////////////////////////////////////////////////////////////
  2984. ' Returns blank if successful else returns error message.
  2985.  
  2986. Function PrintFile$ (sFileName As String, sText As String, bAppend As Integer)
  2987.     'x = 1: y = 2: z$ = "Three"
  2988.  
  2989.     Dim sError As String: sError = ""
  2990.  
  2991.     If Len(sError) = 0 Then
  2992.         If (bAppend = TRUE) Then
  2993.             If _FileExists(sFileName) Then
  2994.                 Open sFileName For Append As #1 ' opens an existing file for appending
  2995.             Else
  2996.                 sError = "Error in PrintFile$ : File not found. Cannot append."
  2997.             End If
  2998.         Else
  2999.             Open sFileName For Output As #1 ' opens and clears an existing file or creates new empty file
  3000.         End If
  3001.     End If
  3002.     If Len(sError) = 0 Then
  3003.         ' WRITE places text in quotes in the file
  3004.         'WRITE #1, x, y, z$
  3005.         'WRITE #1, sText
  3006.  
  3007.         ' PRINT does not put text inside quotes
  3008.         Print #1, sText
  3009.  
  3010.         Close #1
  3011.  
  3012.         'PRINT "File created with data. Press a key!"
  3013.         'K$ = INPUT$(1) 'press a key
  3014.  
  3015.         'OPEN sFileName FOR INPUT AS #2 ' opens a file to read it
  3016.         'INPUT #2, a, b, c$
  3017.         'CLOSE #2
  3018.  
  3019.         'PRINT a, b, c$
  3020.         'WRITE a, b, c$
  3021.     End If
  3022.  
  3023.     PrintFile$ = sError
  3024. End Function ' PrintFile$
  3025.  
  3026. ' /////////////////////////////////////////////////////////////////////////////
  3027. ' Generate random value between Min and Max.
  3028. Function RandomNumber% (Min%, Max%)
  3029.     Dim NumSpread%
  3030.  
  3031.     ' SET RANDOM SEED
  3032.     'Randomize ' Initialize random-number generator.
  3033.  
  3034.     ' GET RANDOM # Min%-Max%
  3035.     'RandomNumber = Int((Max * Rnd) + Min) ' generate number
  3036.  
  3037.     NumSpread% = (Max% - Min%) + 1
  3038.  
  3039.     RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
  3040.  
  3041. End Function ' RandomNumber%
  3042.  
  3043. ' /////////////////////////////////////////////////////////////////////////////
  3044.  
  3045. Sub RandomNumberTest
  3046.     Dim iCols As Integer: iCols = 10
  3047.     Dim iRows As Integer: iRows = 20
  3048.     Dim iLoop As Integer
  3049.     Dim iX As Integer
  3050.     Dim iY As Integer
  3051.     Dim sError As String
  3052.     Dim sFileName As String
  3053.     Dim sText As String
  3054.     Dim bAppend As Integer
  3055.     Dim iMin As Integer
  3056.     Dim iMax As Integer
  3057.     Dim iNum As Integer
  3058.     Dim iErrorCount As Integer
  3059.     Dim sInput$
  3060.  
  3061.     sFileName = "c:\temp\maze_test_1.txt"
  3062.     sText = "Count" + Chr$(9) + "Min" + Chr$(9) + "Max" + Chr$(9) + "Random"
  3063.     bAppend = FALSE
  3064.     sError = PrintFile$(sFileName, sText, bAppend)
  3065.     If Len(sError) = 0 Then
  3066.         bAppend = TRUE
  3067.         iErrorCount = 0
  3068.  
  3069.         iMin = 0
  3070.         iMax = iCols - 1
  3071.         For iLoop = 1 To 100
  3072.             iNum = RandomNumber%(iMin, iMax)
  3073.             sText = Str$(iLoop) + Chr$(9) + Str$(iMin) + Chr$(9) + Str$(iMax) + Chr$(9) + Str$(iNum)
  3074.             sError = PrintFile$(sFileName, sText, bAppend)
  3075.             If Len(sError) > 0 Then
  3076.                 iErrorCount = iErrorCount + 1
  3077.                 Print Str$(iLoop) + ". ERROR"
  3078.                 Print "    " + "iMin=" + Str$(iMin)
  3079.                 Print "    " + "iMax=" + Str$(iMax)
  3080.                 Print "    " + "iNum=" + Str$(iNum)
  3081.                 Print "    " + "Could not write to file " + Chr$(34) + sFileName + Chr$(34) + "."
  3082.                 Print "    " + sError
  3083.             End If
  3084.         Next iLoop
  3085.  
  3086.         iMin = 0
  3087.         iMax = iRows - 1
  3088.         For iLoop = 1 To 100
  3089.             iNum = RandomNumber%(iMin, iMax)
  3090.             sText = Str$(iLoop) + Chr$(9) + Str$(iMin) + Chr$(9) + Str$(iMax) + Chr$(9) + Str$(iNum)
  3091.             sError = PrintFile$(sFileName, sText, bAppend)
  3092.             If Len(sError) > 0 Then
  3093.                 iErrorCount = iErrorCount + 1
  3094.                 Print Str$(iLoop) + ". ERROR"
  3095.                 Print "    " + "iMin=" + Str$(iMin)
  3096.                 Print "    " + "iMax=" + Str$(iMax)
  3097.                 Print "    " + "iNum=" + Str$(iNum)
  3098.                 Print "    " + "Could not write to file " + Chr$(34) + sFileName + Chr$(34) + "."
  3099.                 Print "    " + sError
  3100.             End If
  3101.         Next iLoop
  3102.  
  3103.         Print "Finished generating numbers. Errors: " + Str$(iErrorCount)
  3104.     Else
  3105.         Print "Error creating file " + Chr$(34) + sFileName + Chr$(34) + "."
  3106.         Print sError
  3107.     End If
  3108.  
  3109.     Input "Press <ENTER> to continue", sInput$
  3110. End Sub ' RandomNumberTest
  3111.  
  3112. ' /////////////////////////////////////////////////////////////////////////////
  3113. ' FROM: String Manipulation
  3114. ' found at abandoned, outdated and now likely malicious qb64 dot net website
  3115. ' http://www.qb64.[net]/forum/index_topic_5964-0/
  3116. '
  3117. 'SUMMARY:
  3118. '   Purpose:  A library of custom functions that transform strings.
  3119. '   Author:   Dustinian Camburides (dustinian@gmail.com)
  3120. '   Platform: QB64 (www.qb64.org)
  3121. '   Revision: 1.6
  3122. '   Updated:  5/28/2012
  3123.  
  3124. 'SUMMARY:
  3125. '[Replace$] replaces all instances of the [Find] sub-string with the [Add] sub-string within the [Text] string.
  3126. 'INPUT:
  3127. 'Text: The input string; the text that's being manipulated.
  3128. 'Find: The specified sub-string; the string sought within the [Text] string.
  3129. 'Add: The sub-string that's being added to the [Text] string.
  3130.  
  3131. Function Replace$ (Text1 As String, Find1 As String, Add1 As String)
  3132.     ' VARIABLES:
  3133.     Dim Text2 As String
  3134.     Dim Find2 As String
  3135.     Dim Add2 As String
  3136.     Dim lngLocation As Long ' The address of the [Find] substring within the [Text] string.
  3137.     Dim strBefore As String ' The characters before the string to be replaced.
  3138.     Dim strAfter As String ' The characters after the string to be replaced.
  3139.  
  3140.     ' INITIALIZE:
  3141.     ' MAKE COPIESSO THE ORIGINAL IS NOT MODIFIED (LIKE ByVal IN VBA)
  3142.     Text2 = Text1
  3143.     Find2 = Find1
  3144.     Add2 = Add1
  3145.  
  3146.     lngLocation = InStr(1, Text2, Find2)
  3147.  
  3148.     ' PROCESSING:
  3149.     ' While [Find2] appears in [Text2]...
  3150.     While lngLocation
  3151.         ' Extract all Text2 before the [Find2] substring:
  3152.         strBefore = Left$(Text2, lngLocation - 1)
  3153.  
  3154.         ' Extract all text after the [Find2] substring:
  3155.         strAfter = Right$(Text2, ((Len(Text2) - (lngLocation + Len(Find2) - 1))))
  3156.  
  3157.         ' Return the substring:
  3158.         Text2 = strBefore + Add2 + strAfter
  3159.  
  3160.         ' Locate the next instance of [Find2]:
  3161.         lngLocation = InStr(1, Text2, Find2)
  3162.  
  3163.         ' Next instance of [Find2]...
  3164.     Wend
  3165.  
  3166.     ' OUTPUT:
  3167.     Replace$ = Text2
  3168. End Function ' Replace$
  3169.  
  3170. ' /////////////////////////////////////////////////////////////////////////////
  3171.  
  3172. Sub ReplaceTest
  3173.     Dim in$
  3174.  
  3175.     Print "-------------------------------------------------------------------------------"
  3176.     Print "ReplaceTest"
  3177.     Print
  3178.  
  3179.     Print "Original value"
  3180.     in$ = "Thiz iz a teZt."
  3181.     Print "in$ = " + Chr$(34) + in$ + Chr$(34)
  3182.     Print
  3183.  
  3184.     Print "Replacing lowercase " + Chr$(34) + "z" + Chr$(34) + " with " + Chr$(34) + "s" + Chr$(34) + "..."
  3185.     in$ = Replace$(in$, "z", "s")
  3186.     Print "in$ = " + Chr$(34) + in$ + Chr$(34)
  3187.     Print
  3188.  
  3189.     Print "Replacing uppercase " + Chr$(34) + "Z" + Chr$(34) + " with " + Chr$(34) + "s" + Chr$(34) + "..."
  3190.     in$ = Replace$(in$, "Z", "s")
  3191.     Print "in$ = " + Chr$(34) + in$ + Chr$(34)
  3192.     Print
  3193.  
  3194.     Print "ReplaceTest finished."
  3195. End Sub ' ReplaceTest
  3196.  
  3197. ' /////////////////////////////////////////////////////////////////////////////
  3198. ' https://www.qb64.org/forum/index.php?topic=3605.0
  3199. ' Quote from: SMcNeill on Today at 03:53:48 PM
  3200. '
  3201. ' Sometimes, you guys make things entirely too  complicated.
  3202. ' There ya go!  Three functions to either round naturally,
  3203. ' always round down, or always round up, to whatever number of digits you desire.
  3204. ' EDIT:  Modified to add another option to round scientific,
  3205. ' since you had it's description included in your example.
  3206.  
  3207. Function Round## (num##, digits%)
  3208.     Round## = Int(num## * 10 ^ digits% + .5) / 10 ^ digits%
  3209.  
  3210. Function RoundUp## (num##, digits%)
  3211.     RoundUp## = _Ceil(num## * 10 ^ digits%) / 10 ^ digits%
  3212.  
  3213. Function RoundDown## (num##, digits%)
  3214.     RoundDown## = Int(num## * 10 ^ digits%) / 10 ^ digits%
  3215.  
  3216. Function Round_Scientific## (num##, digits%)
  3217.     Round_Scientific## = _Round(num## * 10 ^ digits%) / 10 ^ digits%
  3218.  
  3219. Function RoundUpDouble# (num#, digits%)
  3220.     RoundUpDouble# = _Ceil(num# * 10 ^ digits%) / 10 ^ digits%
  3221.  
  3222. Function RoundUpSingle! (num!, digits%)
  3223.     RoundUpSingle! = _Ceil(num! * 10 ^ digits%) / 10 ^ digits%
  3224.  
  3225. ' /////////////////////////////////////////////////////////////////////////////
  3226. ' fantastically inefficient way to set a bit
  3227.  
  3228. ' example use: arrMaze(iX, iY) = SetBit256%(arrMaze(iX, iY), cS, FALSE)
  3229.  
  3230. ' See also: GetBit256%, SetBit256%
  3231.  
  3232. ' newint=SetBit256%(oldint, int containing the bits we want to set, value to set them to)
  3233. Function SetBit256% (iNum1 As Integer, iBit1 As Integer, bVal1 As Integer)
  3234.     Dim sNum As String
  3235.     Dim sBit As String
  3236.     Dim sVal As String
  3237.     Dim iLoop As Integer
  3238.     Dim strResult As String
  3239.     Dim iResult As Integer
  3240.     Dim iNum As Integer: iNum = iNum1
  3241.     Dim iBit As Integer: iBit = iBit1
  3242.     Dim bVal As Integer: bVal = bVal1
  3243.  
  3244.     If iNum < 256 And iBit <= 128 Then
  3245.         sNum = GetBinary$(iNum)
  3246.         sBit = GetBinary$(iBit)
  3247.         If bVal = TRUE Then
  3248.             sVal = "1"
  3249.         Else
  3250.             sVal = "0"
  3251.         End If
  3252.         strResult = ""
  3253.         For iLoop = 1 To 8
  3254.             If Mid$(sBit, iLoop, 1) = "1" Then
  3255.                 strResult = strResult + sVal
  3256.             Else
  3257.                 strResult = strResult + Mid$(sNum, iLoop, 1)
  3258.             End If
  3259.         Next iLoop
  3260.         iResult = GetIntegerFromBinary%(strResult)
  3261.     Else
  3262.         iResult = iNum
  3263.     End If
  3264.  
  3265.     SetBit256% = iResult
  3266. End Function ' SetBit256%
  3267.  
  3268. ' /////////////////////////////////////////////////////////////////////////////
  3269. ' Scientific notation - QB64 Wiki
  3270. ' https://www.qb64.org/wiki/Scientific_notation
  3271.  
  3272. ' Example: A string function that displays extremely small or large exponential decimal values.
  3273.  
  3274. Function SngToStr$ (n!)
  3275.     value$ = UCase$(LTrim$(Str$(n!)))
  3276.     Xpos% = InStr(value$, "D") + InStr(value$, "E") 'only D or E can be present
  3277.     If Xpos% Then
  3278.         expo% = Val(Mid$(value$, Xpos% + 1))
  3279.         If Val(value$) < 0 Then
  3280.             sign$ = "-": valu$ = Mid$(value$, 2, Xpos% - 2)
  3281.         Else valu$ = Mid$(value$, 1, Xpos% - 1)
  3282.         End If
  3283.         dot% = InStr(valu$, "."): L% = Len(valu$)
  3284.         If expo% > 0 Then add$ = String$(expo% - (L% - dot%), "0")
  3285.         If expo% < 0 Then min$ = String$(Abs(expo%) - (dot% - 1), "0"): DP$ = "."
  3286.         For n = 1 To L%
  3287.             If Mid$(valu$, n, 1) <> "." Then num$ = num$ + Mid$(valu$, n, 1)
  3288.         Next
  3289.     Else SngToStr$ = value$: Exit Function
  3290.     End If
  3291.     SngToStr$ = _Trim$(sign$ + DP$ + min$ + num$ + add$)
  3292. End Function ' SngToStr$
  3293.  
  3294. ' /////////////////////////////////////////////////////////////////////////////
  3295. ' Split and join strings
  3296. ' https://www.qb64.org/forum/index.php?topic=1073.0
  3297. '
  3298. ' FROM luke, QB64 Developer
  3299. ' Date: February 15, 2019, 04:11:07 AM »
  3300. '
  3301. ' Given a string of words separated by spaces (or any other character),
  3302. ' splits it into an array of the words. I've no doubt many people have
  3303. ' written a version of this over the years and no doubt there's a million
  3304. ' ways to do it, but I thought I'd put mine here so we have at least one
  3305. ' version. There's also a join function that does the opposite
  3306. ' array -> single string.
  3307. '
  3308. ' Code is hopefully reasonably self explanatory with comments and a little demo.
  3309. ' Note, this is akin to Python/JavaScript split/join, PHP explode/implode.
  3310.  
  3311. 'Split in$ into pieces, chopping at every occurrence of delimiter$. Multiple consecutive occurrences
  3312. 'of delimiter$ are treated as a single instance. The chopped pieces are stored in result$().
  3313. '
  3314. 'delimiter$ must be one character long.
  3315. 'result$() must have been REDIMmed previously.
  3316.  
  3317. ' Modified to handle multi-character delimiters
  3318.  
  3319. Sub split (in$, delimiter$, result$())
  3320.     Dim start As Integer
  3321.     Dim finish As Integer
  3322.     Dim iDelimLen As Integer
  3323.     ReDim result$(-1)
  3324.  
  3325.     iDelimLen = Len(delimiter$)
  3326.  
  3327.     start = 1
  3328.     Do
  3329.         'While Mid$(in$, start, 1) = delimiter$
  3330.         While Mid$(in$, start, iDelimLen) = delimiter$
  3331.             'start = start + 1
  3332.             start = start + iDelimLen
  3333.             If start > Len(in$) Then
  3334.                 Exit Sub
  3335.             End If
  3336.         Wend
  3337.         finish = InStr(start, in$, delimiter$)
  3338.         If finish = 0 Then
  3339.             finish = Len(in$) + 1
  3340.         End If
  3341.  
  3342.         ReDim _Preserve result$(0 To UBound(result$) + 1)
  3343.  
  3344.         result$(UBound(result$)) = Mid$(in$, start, finish - start)
  3345.         start = finish + 1
  3346.     Loop While start <= Len(in$)
  3347. End Sub ' split
  3348.  
  3349. ' /////////////////////////////////////////////////////////////////////////////
  3350.  
  3351. Sub SplitTest
  3352.     Dim in$
  3353.     Dim delim$
  3354.     ReDim arrTest$(0)
  3355.     Dim iLoop%
  3356.  
  3357.     delim$ = Chr$(10)
  3358.     in$ = "this" + delim$ + "is" + delim$ + "a" + delim$ + "test"
  3359.     Print "in$ = " + Chr$(34) + in$ + Chr$(34)
  3360.     Print "delim$ = " + Chr$(34) + delim$ + Chr$(34)
  3361.     split in$, delim$, arrTest$()
  3362.  
  3363.     For iLoop% = LBound(arrTest$) To UBound(arrTest$)
  3364.         Print "arrTest$(" + LTrim$(RTrim$(Str$(iLoop%))) + ") = " + Chr$(34) + arrTest$(iLoop%) + Chr$(34)
  3365.     Next iLoop%
  3366.     Print
  3367.     Print "Split test finished."
  3368. End Sub ' SplitTest
  3369.  
  3370. ' /////////////////////////////////////////////////////////////////////////////
  3371.  
  3372. Sub SplitAndReplaceTest
  3373.     Dim in$
  3374.     Dim out$
  3375.     Dim iLoop%
  3376.     ReDim arrTest$(0)
  3377.  
  3378.     Print "-------------------------------------------------------------------------------"
  3379.     Print "SplitAndReplaceTest"
  3380.     Print
  3381.  
  3382.     Print "Original value"
  3383.     in$ = "This line 1 " + Chr$(13) + Chr$(10) + "and line 2" + Chr$(10) + "and line 3 " + Chr$(13) + "finally THE END."
  3384.     out$ = in$
  3385.     out$ = Replace$(out$, Chr$(13), "\r")
  3386.     out$ = Replace$(out$, Chr$(10), "\n")
  3387.     out$ = Replace$(out$, Chr$(9), "\t")
  3388.     Print "in$ = " + Chr$(34) + out$ + Chr$(34)
  3389.     Print
  3390.  
  3391.     Print "Fixing linebreaks..."
  3392.     in$ = Replace$(in$, Chr$(13) + Chr$(10), Chr$(13))
  3393.     in$ = Replace$(in$, Chr$(10), Chr$(13))
  3394.     out$ = in$
  3395.     out$ = Replace$(out$, Chr$(13), "\r")
  3396.     out$ = Replace$(out$, Chr$(10), "\n")
  3397.     out$ = Replace$(out$, Chr$(9), "\t")
  3398.     Print "in$ = " + Chr$(34) + out$ + Chr$(34)
  3399.     Print
  3400.  
  3401.     Print "Splitting up..."
  3402.     split in$, Chr$(13), arrTest$()
  3403.  
  3404.     For iLoop% = LBound(arrTest$) To UBound(arrTest$)
  3405.         out$ = arrTest$(iLoop%)
  3406.         out$ = Replace$(out$, Chr$(13), "\r")
  3407.         out$ = Replace$(out$, Chr$(10), "\n")
  3408.         out$ = Replace$(out$, Chr$(9), "\t")
  3409.         Print "arrTest$(" + cstr$(iLoop%) + ") = " + Chr$(34) + out$ + Chr$(34)
  3410.     Next iLoop%
  3411.     Print
  3412.  
  3413.     Print "SplitAndReplaceTest finished."
  3414. End Sub ' SplitAndReplaceTest
  3415.  
  3416. ' /////////////////////////////////////////////////////////////////////////////
  3417.  
  3418. Function StrPadRight$ (sValue As String, iWidth As Integer)
  3419.     StrPadRight$ = Left$(sValue + String$(iWidth, " "), iWidth)
  3420. End Function ' StrPadRight$
  3421.  
  3422. ' /////////////////////////////////////////////////////////////////////////////
  3423.  
  3424. Function TrueFalse$ (myValue)
  3425.     If myValue = TRUE Then
  3426.         TrueFalse$ = "TRUE"
  3427.     Else
  3428.         TrueFalse$ = "FALSE"
  3429.     End If
  3430. End Function ' TrueFalse$
  3431.  
  3432. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3433. ' END GENERAL PURPOSE ROUTINES
  3434. ' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  3435.  
  3436. ' #END
  3437. ' ################################################################################################################################################################
  3438.  

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
Re: WIP: game input mapping for gamepad, keyboard, v0.90
« Reply #3 on: January 06, 2022, 10:40:41 pm »
If you don't get these figured out soon, perhaps take a look at my Zelda clone work.

I did get those issues figured out, but another one popped up.
I will definitely give your code a look, thanks for your help!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
Re: WIP: game input mapping for gamepad, keyboard, v0.90
« Reply #4 on: January 06, 2022, 11:30:58 pm »
This is the last update of this version of my Zelda clone, as I messed the data structure up and cant get certain effects and extras to work. Also at some point I broke the ability to shoot the sword,(you still can but it throws some errors you have to OK though).

How about just rolling back to the last stable version, and merging the updates back in a little at a time until you get the bugs worked out? Beyond Compare makes it easy.
It looks like you put a lot of work into this and it would be a shame to let it go to waste!