Author Topic: Tag! 2.2 - the chasing game with network multiplayer for up to 8 players  (Read 3968 times)

0 Members and 1 Guest are viewing this topic.

Offline dajan

  • Newbie
  • Posts: 41
    • View Profile
Hi everyone,

just posting a new enhanced version of Tag! game with LAN/internet multiplayer for max. 8 players added.

Previous version of this game is described in this older topic:
https://www.qb64.org/forum/index.php?topic=1161.0

Since I have never worked with network programming before, it ceratinly isn't coded as supposed to be. My main goal was to just get it work and it kind of is working. You just have to have ping from client to host under cca 50ms, otherwise the gameplay is bit jerky. However on LAN it is fluent and well playable.

To host an internet multiplayer game, it is required to have TCP port 7432 open and forwarded to the computer runnig the host game.

So here is the code:
Code: QB64: [Select]
  1. '  .--------------------------------------------------------------------------------------------------.
  2. '  |                                                                                                  |
  3. '  |                                       Tag! v2                                                    |
  4. '  |                                                                                                  |
  5. '  |                                       daniel.janec@gmail.com                                     |
  6. '  |                                                                                                  |
  7. '  `--------------------------------------------------------------------------------------------------'
  8.  
  9. '--------constants----------------------------------------------------------------------------------------------------------------------------------
  10. CONST MENU_BUILDSTR = "2.2.20.01.01"
  11. CONST RES_X = 1280 'screen horizontal resolution, set to -1 to match the dsektop
  12. CONST RES_Y = 720 'screen vertical resolution, set to -1 to match the dsektop
  13. CONST FPS = 100 'set target frames per seconds to 100 for proper physicss speed
  14. CONST DEBUGMODE = 0 'initial state of debug mode
  15. CONST FULLSCREENMODE = 0 'set 1 for fullscreen on startup, other value to window mode
  16. CONST RES_X_TARG = 1920 'world target horizontal resolution
  17. CONST RES_Y_TARG = 1080 'world target vertical resolution
  18. CONST MAX_COL = 247 'max. color number to use of 255
  19. CONST PLAYER_COUNT_INI = 3 'Initial players count
  20. CONST POCET_GUL_INI = 20 'Initial non player balls count
  21. CONST PLAYER_COUNT_MAX = 32 'Maximum players count
  22. CONST PLAYER_NET_MAX = 8 'Max. number of network players
  23. CONST PLAYER_NET_MIN = 2 'First network control enabled player
  24. CONST POCET_GUL_MAX = 32 'Maximum balls count
  25. CONST STRAT_COUNT_MAX = 20 'Maximum players strategies
  26. CONST PLAY_BALL_R = 20 'Players ball size
  27. CONST V_MAX = 300 / FPS 'Max player ball velocity
  28. CONST ACC_X = 10 / FPS 'Balls acceleration X
  29. CONST ACC_Y = 10 / FPS 'Balls accelereation Y
  30. CONST FRICTION = 0.998 'Space fricition coeffifcient
  31. CONST ENERGY_ALL_MIN = 500 'min.starting energy of all players together
  32. CONST ENERGY_PLAYER_MIN = 50 'min. player's starting energy
  33. CONST ENERGY_PLAYER_MAX = 150 'max. player's starting energy
  34. CONST ENERGY_DEC = 5 / FPS 'ball energy decrement
  35. CONST REACTIME_AVG_NORMAL = 0 * FPS 'Average AI reaction time in sec*FPS
  36. CONST CHYBASMERU_NORMAL = 0.0 'Improper reaction rate
  37. CONST PRIESTOR_MIN = PLAY_BALL_R + 100
  38. CONST DELAY_ROUNDSTART = 8 'delay in seconds after round end to accept resume
  39. CONST DELAY_MATCHEND = 20 'delay in seconds after match end to accept resume
  40. CONST FREEZE_TIME = 1.0 * FPS 'player freeze time after getting tag in seconds
  41. CONST SCORE_POS_X = 50 'Score horizontal draw position
  42. CONST SCORE_DELTA_Y = 13 'Score draw - vertictal offset from screen top
  43. CONST SCORE_DELTA_X = 350 'Score draw - players score horizontal offset
  44. CONST SCORE_NAME_LEN = 120 'Score draw - horizontal space for player name
  45. CONST BOOST_V_KOEF = 3.0 'boost factor of extra maximum speed
  46. CONST BOOST_TRVANIE = 1 * FPS 'boost duration
  47. CONST BOOST_TIME = 1 * FPS 'min time between boosts in sec*fps
  48. CONST MENUCURSOR_COLOR = 15 'Menu cursor color
  49. CONST MENUITEM_COLOR = 7 'Menu cursor color
  50. CONST MENU_PLAYERS_COUNT = 8 'Number of players in menu
  51. CONST FONT_W = 8 'Font width in pixels
  52. CONST FONT_H = 16 'Font height in pixels
  53. CONST GAMESTICK_DEADZONE = 0.05 'gamepad stick axis maximum value considered as zero
  54. CONST CPU_CONTROLID = PLAYER_NET_MAX + 1 'CPU controller id
  55. CONST HOSTIP_DEFAULT = "" '"localhost" '
  56. CONST HOSTPORT_DEFAULT = 7432
  57. CONST GETITEM_SYNC = "SNC" 'network trasfer item code
  58. CONST GETITEM_STAV = "SNS" 'network trasfer item code
  59. CONST GETITEM_BAL = "SNB" 'network trasfer item code
  60. CONST GETITEM_PBAL = "SNP" 'network trasfer item code
  61. CONST GETITEM_KLAV = "SNK" 'network trasfer item code
  62. CONST GETITEM_HRAC = "SNH" 'network trasfer item code
  63. CONST GETITEM_MESG = "SNM" 'network trasfer item code
  64. CONST GETITEM_STAT = "SNT" 'network trasfer item code
  65. CONST NET_TIMEOUT_LIMIT = 10 'timeout to disconnect in seconds
  66. CONST NET_TIMEOUT_SYNC = 0.01 'timeout to get synced in seconds
  67. CONST MSG_COUNT_MAX = 20 'max. messages displayed
  68. CONST MSG_LEN_MAX = 80 'max. length of message
  69. CONST MSG_DUR_STD = 20 'message standard duration in seconds
  70. CONST MSG_COL = 7 'message standard color
  71. CONST UPDATE_PER_BAL = 0.1 'all balls network update period in seconds
  72. CONST UPDATE_PER_PLBAL = 0.04 'player balls network update period in seconds
  73. CONST UPDATE_PER_HRAC = 0.1 'players network update period in seconds
  74. CONST UPDATE_PER_STAV = 0.04 'world state network update period in seconds
  75. CONST AUTOSTART_SEC_MAX = 180 'max game autostart time
  76.  
  77. '--------type definitions----------------------------------------------------------------------------------------------------------------------------
  78. TYPE vektor 'general 2D vector type
  79.     x AS SINGLE
  80.     y AS SINGLE
  81.  
  82. TYPE tstav 'game world state type  (cca 100B)
  83.     minx AS INTEGER 'world - minimum x position
  84.     maxx AS INTEGER 'world - maximum x position
  85.     miny AS INTEGER 'world - minimum y position
  86.     maxy AS INTEGER 'world - maximum y position
  87.     vmax AS SINGLE 'maximum unboosted ball velocity
  88.     maxcol AS INTEGER 'world - maximum of colors
  89.     pocetgulzvoleny AS INTEGER 'number of balls desired
  90.     pocetgul AS INTEGER 'number of balls actual
  91.     pocetgulx AS INTEGER 'number of balls in bottom row
  92.     pocetguly AS INTEGER 'number of balls in left column
  93.     pocethracov AS INTEGER 'number of players
  94.     energy_allmin AS SINGLE 'all players starting energy sum
  95.     energy_playermin AS SINGLE 'minimum starting energy of player
  96.     energy_playermax AS SINGLE 'maximum starting energy of player
  97.     energy_player_start AS SINGLE 'player's starting energy
  98.     energydec AS SINGLE 'energy decrease unit
  99.     baljeto AS INTEGER 'actual tagged ball idx
  100.     balbudeto AS INTEGER 'successor tagged ball idx
  101.     vitaz AS INTEGER 'winner player idx
  102.     koniec_kola AS _BYTE 'end of round flag (0=no end, 1=end of round, 2=end fo round and match)
  103.     odpor AS SINGLE 'space friction
  104.     massallowed AS _BYTE 'calculate balls with different masses
  105.     pauza AS _BYTE 'game pause flag (0 or 1)
  106.     gameplay AS _BYTE 'state of session: 0=menu, 1=gameplay
  107.     fps AS SINGLE 'fps target set
  108.     zacinabal AS INTEGER 'initially tagged ball idx
  109.     boostallowed AS _BYTE 'player boost mode allowed (0 or 1)
  110.     syncstr AS STRING * 3 'net synchronisation string code
  111.     synctime AS DOUBLE 'net synchronisation time stamp
  112.     AIallowed AS _BYTE 'AI players in netgame allowed=1
  113.     autostart_set AS INTEGER 'game autostart setting in seconds; 0=off
  114.     autostart_timeleft AS SINGLE 'actual time to autostart the game in seconds
  115.     winscore AS _BYTE 'score to win a match
  116.  
  117. TYPE gula 'ball  type  (cca 90B)
  118.     i AS INTEGER 'balls id
  119.     x AS SINGLE 'balls y position
  120.     y AS SINGLE 'balls y position
  121.     r AS SINGLE 'radius
  122.     m AS SINGLE 'mass
  123.     v AS vektor 'velocity
  124.     vi AS vektor 'desired velocity
  125.     a AS vektor 'acceleration
  126.     col AS INTEGER 'balls color
  127.     riadena AS INTEGER 'is actually player controllable (0 or 1)
  128.     complayer AS INTEGER 'is computer player's ball (0 or 1)
  129.     protivnik AS INTEGER 'actual ball's enemy ball id
  130.     strat AS INTEGER 'balls behaviour strategy id
  131.     energia AS SINGLE 'players energy
  132.     freezedtime AS INTEGER 'actual time to unfreeze players control
  133.     smer_odchyl AS SINGLE 'actual deviance from intended direction in radians
  134.     klucka_time AS INTEGER 'actual time to start doging
  135.     klucka_uhol AS SINGLE 'actual dodge angle
  136.     klucka_trvanie AS INTEGER 'actual dodge duration
  137.     klucka_pocet AS INTEGER 'actual dodge count
  138.     boost_time AS INTEGER 'actual time to boost allowed again
  139.     boost_trvanie AS INTEGER 'actual time to end boost
  140.     reactdrop_time AS INTEGER 'actual time to start reaction drop in frames
  141.     reactdrop_dur AS INTEGER 'actual time to stop reaction drop in frames
  142.     boostintended AS INTEGER 'intention to boost or not (1, 0)
  143.     syncstr AS STRING * 3 'net synchronisation string code
  144.     synctime AS DOUBLE 'net synchronisation time stamp
  145.     hrac AS INTEGER 'ball player's id
  146.  
  147. TYPE tstrat 'balls behaviour strategy type
  148.     okraj AS INTEGER 'distance from screen border to turn
  149.     okraj_rot AS SINGLE 'distance from screen border to turn
  150.     chybasmeru AS SINGLE 'percentage of deviance from desired direction -- not used currently
  151.     react_lag AS INTEGER 'average reaction time in frames
  152.     klucka_avgtm AS SINGLE 'average time between dodging in fps +-50%
  153.     klucka_uhol AS SINGLE 'average dodge angle
  154.     klucka_trvanie AS INTEGER 'average dodge time +-50%
  155.     klucka_pocet AS INTEGER 'average dodge count
  156.     klucka_uholodch AS SINGLE 'dodge angle deviation percentage
  157.     detekcia_sin AS SINGLE 'sinus of frontal enemy detection angle
  158.     detekcia_vzdfull AS SINGLE 'distance of frontal enemy full detection
  159.     detekcia_vzdmin AS SINGLE 'distance of frontal enemy minimum detection
  160.     detekcia_rot AS SINGLE 'amount of rotation on enemy detection in radians
  161.     reactdrop_per AS INTEGER 'average reaction drop period in frames
  162.     reactdrop_dur AS INTEGER 'average reaction drop duration in frames
  163.     boost_rate AS SINGLE 'inclination to use boost in percent
  164.     boost_dist AS SINGLE 'enemy distance activating boost deciding
  165.     boost_chasesin AS SINGLE 'max. sinus of enemy to player velocity angle to apply chasing boost
  166.     mate_cosenmax AS SINGLE 'max. cosinus of (player-enemy vector to player velocity) to rotate to mate
  167.     mate_rot AS SINGLE 'amount of unit rotation towards nearest mate in rad per frame
  168.     mate_en_distmin AS SINGLE 'min. distance from enemy to apply rotation towards mate
  169.     mate_distmin AS SINGLE 'min. distance from mate to apply rotation towards mate
  170.     adrenalin_rate AS SINGLE 'percentage of reaction time improved in adrenalin mode
  171.     adrenalin_energy AS SINGLE 'energy level precentage starting adrenalin mode
  172.  
  173. TYPE thrac 'player type
  174.     i AS INTEGER 'player index number
  175.     meno AS STRING * 10 'players name
  176.     skore AS _BYTE 'players score
  177.     klavesy AS _BYTE 'players control keys set index
  178.     bal AS INTEGER 'player driven ball idx
  179.     col AS INTEGER 'player's choosed color
  180.     clientid AS _BYTE 'index of hosted client, 0 if local player
  181.     isready AS _BYTE '1=player is ready for game start
  182.     syncstr AS STRING * 3
  183.     synctime AS DOUBLE
  184.  
  185. TYPE tklavesy 'players controller type
  186.     id AS INTEGER 'controllers id
  187.     devid AS INTEGER 'controllers device id  (0=unused)
  188.     typ AS _BYTE 'type of controller (0=unused, 1=keyboard, 2=mouse reserved, 3=gamepad, 4=AI, 5=network, 6=empty)
  189.     nazov AS STRING * 25 'game controllers name
  190.     up AS LONG 'key code for up
  191.     down AS LONG 'keyp code for down
  192.     right AS LONG 'key code for right
  193.     left AS LONG 'key code for left
  194.     shoota AS LONG 'keyborad key code or gamepad button id
  195.     shootb AS LONG 'keyborad key code or gamepad button id
  196.     xaxis AS _BYTE 'gamepad analog x-asis id
  197.     yaxis AS _BYTE 'gamepad analog y-asis id
  198.     dxaxis AS _BYTE 'gamepad digital x-asis id
  199.     dyaxis AS _BYTE 'gamepad digital y-asis id
  200.     isup AS _BYTE 'controllers actual state for up
  201.     isdown AS _BYTE 'controllers actual state for down
  202.     isright AS _BYTE 'controllers actual state for right
  203.     isleft AS _BYTE 'controllers actual state for left
  204.     isshoota AS _BYTE 'controllers actual state for fire 1
  205.     isshootb AS _BYTE 'controllers actual state for fire 2
  206.     gvektor AS vektor 'controllers actual x and y axis state
  207.     buttonstate AS LONG 'all gamepads buttons cumulative state value
  208.     buttonstatechanged AS _BYTE '0 if no button state change, 1 if any button state changed
  209.     buttonstatetimer AS INTEGER
  210.     syncstr AS STRING * 3 'net synchronisation string code
  211.     synctime AS DOUBLE 'net synchronisation time stamp
  212.  
  213. TYPE tmenu
  214.     title AS STRING * 10 'menu title use for nothing
  215.     items_count AS INTEGER 'menu items count
  216.     item_active AS INTEGER 'active item index
  217.     item_height AS INTEGER 'menu item height in pixels
  218.     cursor_col AS INTEGER 'menu cursor color
  219.     cursor_xpos AS INTEGER 'x position of cursors left side
  220.     cursor_out AS INTEGER 'cursor bar outset
  221.     ypos AS INTEGER 'y position of cursor
  222.     cursor_width AS INTEGER 'cursor width in pixels
  223.     menucode AS INTEGER 'stores code of selected action upon active menuitem
  224.     playercode AS INTEGER 'stores  player id of active menuitem
  225.  
  226. TYPE tmessage
  227.     clientid AS INTEGER 'client origin of message
  228.     text AS STRING * MSG_LEN_MAX 'message text
  229.     time_start AS DOUBLE 'message time
  230.     time_dur AS SINGLE 'message duration
  231.     col AS INTEGER 'message color
  232.     distribute AS INTEGER 'message dsitribution status 1=distribute
  233.     syncstr AS STRING * 3 'net synchronisation string code
  234.     synctime AS DOUBLE 'net synchronisation time stamp
  235.  
  236. TYPE tgamesession 'my game session properties
  237.     maxx AS INTEGER 'my screen x resolution
  238.     maxy AS INTEGER 'my screen y resolution
  239.     isnetgame AS _BYTE 'network game flag; 1=network, 0=local
  240.     hosth AS INTEGER 'my host handle
  241.     clienth AS INTEGER 'my client handle
  242.     clientid AS INTEGER 'my client's id registered at host
  243.     host_ip AS STRING * 100 'host's ip to connect as a client
  244.     host_port AS LONG 'host's port to connect as a client
  245.     get_item AS STRING * 3 'actual item to get from host
  246.     get_time AS DOUBLE 'time of last succesfull reading from host
  247.     get_isdesync AS INTEGER 'net transfer is desynced=1
  248.     fpsactual AS SINGLE 'fps achieved
  249.     fpscoef AS SINGLE 'fps actual/target
  250.     tstart AS DOUBLE 'timer start time
  251.     tend AS DOUBLE 'timer end time
  252.     pocetgamepadov AS INTEGER 'number of gamepads detected
  253.     pocetkontrolerov AS INTEGER 'number of game controllers
  254.     start AS INTEGER 'start game flag (0 or 1)
  255.     exituj AS INTEGER 'exit game flag (0 or 1)
  256.     printdebug AS INTEGER 'debug mode flag (0 or 1)
  257.     msgcount AS INTEGER 'total number of actual messages
  258.     msgwrite AS INTEGER 'message write mode=1
  259.     newmsg AS INTEGER 'is new messages to distribute flag
  260.     msgtext AS STRING * MSG_LEN_MAX 'message text being actually written
  261.     autostart_time AS SINGLE 'local host time of game autostart
  262.     timer_pballs AS SINGLE 'local time of last player balls update from host
  263.     sync_stats AS _BYTE 'send statistics to clients flag
  264.     show_stats AS _BYTE 'display match statistics
  265.  
  266. TYPE tclient 'hosted client type
  267.     conn AS INTEGER 'connection handle of hosted client
  268.     ip AS STRING * 100 'ip of hosted client
  269.     get_item AS STRING * 3 'next item to get from client
  270.     get_time AS DOUBLE 'time of last succesfull reading from client
  271.     get_isdesync AS INTEGER 'net desynchronisation flag host cannot get from client
  272.     isdesynced AS _BYTE 'client side is desynced - cannot get from host
  273.     spectating AS _BYTE 'client player control enabled
  274.  
  275. TYPE tsyncvar
  276.     syncstr AS STRING * 3 'sync string code
  277.     synctime AS DOUBLE 'net synchronisation time stamp
  278.     itemstr AS STRING * 3 'code of the next item to get
  279.     isdesync AS _BYTE '1=the other side is desynced
  280.     clientid AS INTEGER 'client id
  281.  
  282. '-----global variables-------------------------------------------------------------------------------------------------------------------------------
  283. REDIM SHARED bal(1 TO POCET_GUL_MAX + PLAYER_COUNT_MAX) AS gula 'balls
  284. DIM SHARED klav(1 TO PLAYER_COUNT_MAX) AS tklavesy 'keyboard control sets
  285. DIM SHARED stav AS tstav 'game world state
  286. DIM SHARED strat(1 TO STRAT_COUNT_MAX) AS tstrat 'computer players strategies
  287. DIM SHARED hrac(1 TO PLAYER_COUNT_MAX) AS thrac 'players balls
  288. DIM SHARED sync AS tsyncvar 'net sync variable
  289. DIM SHARED stats(1 TO PLAYER_NET_MAX + 1, 1 TO PLAYER_NET_MAX) AS INTEGER 'players game statistics
  290.  
  291. REDIM SHARED ibal(1 TO POCET_GUL_MAX) AS gula 'nonplayer balls transfer array
  292. REDIM SHARED ipbal(1 TO PLAYER_NET_MAX) AS gula 'players balls transfer array
  293. DIM SHARED iklav(1 TO PLAYER_COUNT_MAX) AS tklavesy 'keyboard control sets transfer array
  294. DIM SHARED istav AS tstav 'game world state transfer array
  295. DIM SHARED ihrac(1 TO PLAYER_NET_MAX) AS thrac 'players balls transfer array
  296. DIM SHARED isync AS tsyncvar 'net sync variable transfer array
  297. DIM SHARED istats(1 TO PLAYER_NET_MAX + 1, 1 TO PLAYER_NET_MAX) AS INTEGER 'players game statistics transfer
  298.  
  299. DIM SHARED client(1 TO 8) AS tclient
  300. DIM SHARED menu AS tmenu
  301. DIM SHARED game AS tgamesession
  302. DIM SHARED msg(1 TO MSG_COUNT_MAX) AS tmessage
  303.  
  304. '----main program------------------------------------------------------------------------------------------------------------------------------------
  305. SetGlobalParameters
  306. SetUpControllers
  307. IF game.pocetgamepadov > 0 THEN hrac(1).klavesy = game.pocetkontrolerov 'set gamepad as default controller for player1
  308.  
  309. SCREEN _NEWIMAGE(game.maxx, game.maxy, 256), , ,
  310. IF FULLSCREENMODE = 1 THEN _FULLSCREEN
  311. AddMessage "Welcome in the Tag game.", MSG_COL, 5, 0
  312. AddMessage "To display/hide match statistics press 'c' while gameplay.", MSG_COL, 8, 0
  313. AddMessage "Default TCP port for the network game is 7432. ", MSG_COL, 11, 0
  314.  
  315.     '--------Main menu begin---------------------------------------------------------------------------------------------------------------------------------------------------------------------
  316.     REDIM SHARED ibal(1 TO POCET_GUL_MAX) AS gula 'nonplayer balls transfer array
  317.     REDIM SHARED ipbal(1 TO PLAYER_NET_MAX) AS gula 'players balls transfer array
  318.     DIM playerst(1 TO 8) AS STRING * 30
  319.     DIM hoststat AS STRING * 50
  320.     DIM joinstat AS STRING * 50
  321.     DIM autostart AS STRING
  322.     DIM AIplayers AS STRING
  323.     DIM newenergy AS STRING * 3
  324.  
  325.     stav.gameplay = 0
  326.     vys = FONT_H
  327.     sir = FONT_W
  328.     menu.items_count = 27
  329.  
  330.     DO
  331.         WINDOW (0, 0)-(game.maxx, game.maxy)
  332.         IF game.hosth <> 0 THEN hoststat = "host started" ELSE hoststat = ""
  333.         IF game.clienth <> 0 AND game.clientid = 0 THEN joinstat = "joining game...   "
  334.         IF game.clienth <> 0 AND game.clientid <> 0 THEN joinstat = "connected to         " + LEFT$(game.host_ip, 30)
  335.         IF game.clienth = 0 AND game.clientid = 0 THEN joinstat = ""
  336.         ReadyPlayersCount = 0
  337.         FOR i = 1 TO 8
  338.             playerst(i) = ""
  339.             IF game.isnetgame = 1 THEN
  340.                 IF hrac(i).clientid > 0 THEN playerst(i) = "  state: connected" ELSE playerst(i) = "  state: "
  341.                 IF klav(hrac(i).klavesy).typ <= 3 THEN playerst(i) = "  state: local player"
  342.                 IF hrac(i).isready = 1 AND hrac(i).clientid > 0 THEN playerst(i) = RTRIM$(playerst(i)) + " / ready!": ReadyPlayersCount = ReadyPlayersCount + 1
  343.             END IF
  344.         NEXT i
  345.  
  346.         IF stav.autostart_set = 0 THEN
  347.             autostart = "Off"
  348.         ELSE
  349.             HumanPlayers = HumanPlayersCount
  350.             IF game.autostart_time - stav.autostart_set > TIMER THEN game.autostart_time = game.autostart_time - 86399
  351.             IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) THEN stav.autostart_timeleft = game.autostart_time - TIMER
  352.             IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) AND game.autostart_time <= TIMER THEN
  353.                 IF stav.AIallowed = 1 OR (stav.AIallowed = 0 AND HumanPlayers >= 2) THEN
  354.                     game.start = 1
  355.                 ELSE
  356.                     game.autostart_time = TIMER + stav.autostart_set
  357.                 END IF
  358.             END IF
  359.             IF game.isnetgame = 1 AND game.hosth <> 0 AND ReadyPlayersCount = HumanPlayers AND ((stav.AIallowed = 1 AND HumanPlayers >= 1) OR (stav.AIallowed = 0 AND HumanPlayers >= 2)) THEN game.start = 1
  360.             autostart = LTRIM$(STR$(stav.autostart_set)) + " sec               " + LTRIM$(STR$(INT(stav.autostart_timeleft))) + " left"
  361.         END IF
  362.  
  363.         IF stav.AIallowed = 1 THEN AIplayers = "Yes" ELSE AIplayers = "No"
  364.  
  365.         CLS 1
  366.         DisplayMessages
  367.         PrintMenuItem -2, menu, 0, 0, 0, "Tag! v.2", 0, 0, -3, 13
  368.         PrintMenuItem -1, menu, 0, 0, 0, "created in QuickBasic64 by Daniel Janec (2019) build " + MENU_BUILDSTR, 0, 0, -2, 20
  369.         PrintMenuItem 1, menu, 6, 0, 0, "Play <Space>", 0, 0, 0, MENUITEM_COLOR
  370.         PrintMenuItem 2, menu, 1, 0, 0, "Refresh gamepads detected: " + STR$(game.pocetgamepadov), 0, 0, 0, MENUITEM_COLOR
  371.         PrintMenuItem 3, menu, 2, 1, 1, hrac(1).meno + " controls: " + klav(hrac(1).klavesy).nazov + RTRIM$(playerst(1)), 150, -10 * sir, 1, hrac(1).col
  372.         PrintMenuItem 4, menu, 3, 1, 1, "              color:" + STR$(hrac(1).col), 150, -10 * sir, 1, hrac(1).col
  373.         PrintMenuItem 5, menu, 2, 2, 1, hrac(2).meno + " controls: " + klav(hrac(2).klavesy).nazov + RTRIM$(playerst(2)), 150, -10 * sir, 1, hrac(2).col
  374.         PrintMenuItem 6, menu, 3, 2, 1, "              color:" + STR$(hrac(2).col), 150, -10 * sir, 1, hrac(2).col
  375.         PrintMenuItem 7, menu, 2, 3, 1, hrac(3).meno + " controls: " + klav(hrac(3).klavesy).nazov + RTRIM$(playerst(3)), 150, -10 * sir, 1, hrac(3).col
  376.         PrintMenuItem 8, menu, 3, 3, 1, "              color:" + STR$(hrac(3).col), 150, -10 * sir, 1, hrac(3).col
  377.         PrintMenuItem 9, menu, 2, 4, 1, hrac(4).meno + " controls: " + klav(hrac(4).klavesy).nazov + RTRIM$(playerst(4)), 150, -10 * sir, 1, hrac(4).col
  378.         PrintMenuItem 10, menu, 3, 4, 1, "              color:" + STR$(hrac(4).col), 150, -10 * sir, 1, hrac(4).col
  379.         PrintMenuItem 11, menu, 2, 5, 1, hrac(5).meno + " controls: " + klav(hrac(5).klavesy).nazov + RTRIM$(playerst(5)), 150, -10 * sir, 1, hrac(5).col
  380.         PrintMenuItem 12, menu, 3, 5, 1, "              color:" + STR$(hrac(5).col), 150, -10 * sir, 1, hrac(5).col
  381.         PrintMenuItem 13, menu, 2, 6, 1, hrac(6).meno + " controls: " + klav(hrac(6).klavesy).nazov + RTRIM$(playerst(6)), 150, -10 * sir, 1, hrac(6).col
  382.         PrintMenuItem 14, menu, 3, 6, 1, "              color:" + STR$(hrac(6).col), 150, -10 * sir, 1, hrac(6).col
  383.         PrintMenuItem 15, menu, 2, 7, 1, hrac(7).meno + " controls: " + klav(hrac(7).klavesy).nazov + RTRIM$(playerst(7)), 150, -10 * sir, 1, hrac(7).col
  384.         PrintMenuItem 16, menu, 3, 7, 1, "              color:" + STR$(hrac(7).col), 150, -10 * sir, 1, hrac(7).col
  385.         PrintMenuItem 17, menu, 2, 8, 1, hrac(8).meno + " controls: " + klav(hrac(8).klavesy).nazov + RTRIM$(playerst(8)), 150, -10 * sir, 1, hrac(8).col
  386.         PrintMenuItem 18, menu, 3, 8, 1, "              color:" + STR$(hrac(8).col), 150, -10 * sir, 1, hrac(8).col
  387.         PrintMenuItem 19, menu, 8, 0, 1, "Player starting energy: " + LTRIM$(STR$(stav.energy_player_start)), 100, -10 * sir, 2, MENUITEM_COLOR
  388.         PrintMenuItem 20, menu, 4, 0, 1, "     Number of players: " + LTRIM$(STR$(stav.pocethracov)), 100, -10 * sir, 2, MENUITEM_COLOR
  389.         PrintMenuItem 21, menu, 12, 0, 1, "   CPU players allowed: " + AIplayers, 100, -10 * sir, 2, MENUITEM_COLOR
  390.         PrintMenuItem 22, menu, 5, 0, 1, "           Balls count: " + LTRIM$(STR$(stav.pocetgulzvoleny)), 100, -10 * sir, 2, MENUITEM_COLOR
  391.         PrintMenuItem 23, menu, 13, 0, 1, "   Match winning score: " + LTRIM$(STR$(stav.winscore)), 100, -10 * sir, 2, MENUITEM_COLOR
  392.         PrintMenuItem 24, menu, 11, 0, 1, "        Game autostart: " + autostart, 100, -10 * sir, 2, MENUITEM_COLOR
  393.         PrintMenuItem 25, menu, 9, 0, 0, "             Host game: " + hoststat, 100, -10 * sir, 2, MENUITEM_COLOR
  394.         PrintMenuItem 26, menu, 10, 0, 0, "             Join game: " + joinstat, 100, -10 * sir, 2, MENUITEM_COLOR
  395.         PrintMenuItem 27, menu, 7, 0, 0, "Exit <Esc>", 0, 0, 2, MENUITEM_COLOR
  396.         PrintMenuCursor menu
  397.  
  398.         k = _KEYHIT 'read menu action
  399.         IF game.msgwrite = 1 AND game.isnetgame = 1 THEN WriteMessage k: k = 0
  400.         IF game.msgwrite = 0 AND game.isnetgame = 1 THEN WriteMessage -1
  401.  
  402.         '---debug code begin---------------------------------------------
  403.         IF k = 98 THEN game.printdebug = -(game.printdebug = 0)
  404.         IF game.printdebug = 1 THEN DebugScreen k 'print debug info on screen
  405.         '---debug code end-----------------------------------------------
  406.  
  407.         FOR i = 1 TO game.pocetkontrolerov
  408.             IF klav(i).typ = 3 THEN
  409.                 ReadGamepad klav(i)
  410.                 IF klav(i).buttonstatechanged THEN
  411.                     IF klav(i).isshoota THEN k = 13: klav(i).isshoota = 0
  412.                     IF klav(i).isup THEN k = 18432: klav(i).isup = 0
  413.                     IF klav(i).isdown THEN k = 20480: klav(i).isdown = 0
  414.                     IF klav(i).isright THEN k = 19712: klav(i).isright = 0
  415.                     IF klav(i).isleft THEN k = 19200: klav(i).isleft = 0
  416.                 END IF
  417.             END IF
  418.         NEXT i
  419.  
  420.         SELECT CASE k 'perform menu choice
  421.             CASE 18432 'up
  422.                 IF menu.item_active > 1 THEN menu.item_active = menu.item_active - 1
  423.             CASE 20480 'down
  424.                 IF menu.item_active < menu.items_count THEN menu.item_active = menu.item_active + 1
  425.             CASE 19200 'left
  426.                 SELECT CASE menu.menucode
  427.                     CASE 2 'players controller
  428.                         i = menu.playercode
  429.                         hrac(i).klavesy = hrac(i).klavesy - 1
  430.                         IF hrac(i).klavesy < PLAYER_NET_MAX + 1 THEN hrac(i).klavesy = PLAYER_NET_MAX + 1
  431.                     CASE 3 'players color
  432.                         i = menu.playercode
  433.                         hrac(i).col = hrac(i).col - 1
  434.                         IF hrac(i).col < 1 THEN hrac(i).col = stav.maxcol
  435.                     CASE 4 'players count
  436.                         IF stav.pocethracov > 2 THEN
  437.                             stav.pocethracov = stav.pocethracov - 1
  438.                         END IF
  439.                     CASE 5 'balls count
  440.                         IF stav.pocetgulzvoleny > 0 THEN
  441.                             stav.pocetgulzvoleny = stav.pocetgulzvoleny - 1
  442.                         END IF
  443.                     CASE 8 'starting energy
  444.                         IF stav.energy_player_start > 10 THEN
  445.                             stav.energy_player_start = stav.energy_player_start - 1
  446.                         END IF
  447.                     CASE 11 'game autostart
  448.                         IF stav.autostart_set > 0 THEN
  449.                             stav.autostart_set = stav.autostart_set - 10
  450.                             game.autostart_time = TIMER + stav.autostart_set
  451.                         END IF
  452.                     CASE 12 'CPU players allowed
  453.                         stav.AIallowed = 0
  454.                     CASE 13 'Winning score
  455.                         IF stav.winscore > 1 THEN stav.winscore = stav.winscore - 1
  456.                 END SELECT
  457.             CASE 19712 'right
  458.                 SELECT CASE menu.menucode
  459.                     CASE 2 'players controller
  460.                         i = menu.playercode
  461.                         hrac(i).klavesy = hrac(i).klavesy + 1
  462.                         IF hrac(i).klavesy > game.pocetkontrolerov THEN hrac(i).klavesy = game.pocetkontrolerov
  463.                     CASE 3 'players color
  464.                         i = menu.playercode
  465.                         hrac(i).col = hrac(i).col + 1
  466.                         IF hrac(i).col > stav.maxcol THEN hrac(i).col = 1
  467.                     CASE 4 'players count
  468.                         IF stav.pocethracov < PLAYER_COUNT_MAX THEN
  469.                             stav.pocethracov = stav.pocethracov + 1
  470.                         END IF
  471.                     CASE 5 'balls count
  472.                         IF stav.pocetgulzvoleny < POCET_GUL_MAX THEN
  473.                             stav.pocetgulzvoleny = stav.pocetgulzvoleny + 1
  474.                         END IF
  475.                     CASE 8 'starting energy
  476.                         IF stav.energy_player_start < ENERGY_PLAYER_MAX THEN
  477.                             stav.energy_player_start = stav.energy_player_start + 1
  478.                         END IF
  479.                     CASE 11 'game autostart
  480.                         IF stav.autostart_set < AUTOSTART_SEC_MAX THEN
  481.                             stav.autostart_set = stav.autostart_set + 10
  482.                             game.autostart_time = TIMER + stav.autostart_set
  483.                         END IF
  484.                     CASE 12 'CPU players allowed
  485.                         stav.AIallowed = 1
  486.                     CASE 13 'Winning score
  487.                         IF stav.winscore < 99 THEN stav.winscore = stav.winscore + 1
  488.                 END SELECT
  489.             CASE 13 'enter
  490.                 SELECT CASE menu.menucode
  491.                     CASE 1
  492.                         SetUpControllers
  493.                     CASE 8 'starting energy
  494.                         newenergy = LTRIM$(STR$(stav.energy_player_start))
  495.                         newenergy = InputText$(newenergy, menu.cursor_xpos + 2 * sir, menu.ypos + (menu.item_active - 1) * menu.item_height, MENUITEM_COLOR, 48, "<Type starting energy>", 0, 1)
  496.                         IF LEFT$(newenergy$, 1) <> CHR$(27) AND LEN(RTRIM$(newenergy$)) > 0 THEN
  497.                             energyval = VAL(newenergy)
  498.                             IF energyval < 10 THEN energyval = 10
  499.                             IF energyval > ENERGY_PLAYER_MAX THEN energyval = ENERGY_PLAYER_MAX
  500.                             stav.energy_player_start = energyval
  501.                         END IF
  502.                     CASE 2 'set new name
  503.                         novemeno$ = InputText$(hrac(menu.playercode).meno, menu.cursor_xpos + 2 * sir, menu.ypos + (menu.item_active - 1) * menu.item_height, hrac(menu.playercode).col, 14, "<Type name>", 0, 0)
  504.                         IF novemeno$ <> CHR$(27) AND LEN(novemeno$) > 0 THEN hrac(menu.playercode).meno = novemeno$
  505.                     CASE 9 'host game
  506.                         HostGame
  507.                     CASE 10 'join game
  508.                         IF game.clienth = 0 THEN
  509.                             gethostip$ = InputText$(game.host_ip, menu.cursor_xpos + 2 * sir, menu.ypos + (menu.item_active - 1) * menu.item_height, MENUITEM_COLOR, LEN(game.host_ip), "<Type IP address or host name>", 1, 0)
  510.                             IF gethostip$ <> CHR$(27) THEN game.host_ip = gethostip$
  511.                             IF gethostip$ <> CHR$(27) AND LEN(RTRIM$(game.host_ip)) > 0 THEN JoinGame
  512.                         ELSE
  513.                             ResetNetGame
  514.                         END IF
  515.                     CASE 6 'start game
  516.                         IF game.clientid > 0 THEN hrac(game.clientid).isready = 1 - hrac(game.clientid).isready
  517.                         IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) AND (stav.AIallowed = 1 OR (stav.AIallowed = 0 AND HumanPlayersCount >= 2)) THEN game.start = 1
  518.                     CASE 7 'exit game
  519.                         game.exituj = 1
  520.                 END SELECT
  521.             CASE 116 'messaage write mode on
  522.                 IF game.isnetgame = 1 THEN
  523.                     game.msgwrite = 1
  524.                     k = 0
  525.                 END IF
  526.             CASE 32 'start game
  527.                 IF game.clientid > 0 THEN hrac(game.clientid).isready = 1 - hrac(game.clientid).isready
  528.                 IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) AND (stav.AIallowed = 1 OR (stav.AIallowed = 0 AND HumanPlayersCount >= 2)) THEN game.start = 1
  529.             CASE 27 'exit game
  530.                 game.exituj = 1
  531.         END SELECT
  532.  
  533.         FOR i = 1 TO PLAYER_COUNT_MAX 'set AI controller type for computer players
  534.             IF game.isnetgame = 1 AND game.clienth <> 0 AND game.clientid <> 0 AND klav(hrac(i).klavesy).typ = 4 THEN hrac(i).klavesy = hrac(i).klavesy + 1 'change AI controler on client to keyboard
  535.             IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) AND klav(hrac(i).klavesy).typ = 4 THEN 'on host or singleplayer change AI controlled ball
  536.                 bal(hrac(i).bal).complayer = 1
  537.             ELSE
  538.                 bal(hrac(i).bal).complayer = 0
  539.             END IF
  540.         NEXT i
  541.  
  542.         IF game.isnetgame AND stav.pocethracov > PLAYER_NET_MAX THEN stav.pocethracov = PLAYER_NET_MAX
  543.         IF menu.menucode = 4 AND (k = 19712 OR k = 19200) THEN 'after changing players count
  544.             stav.energy_player_start = INT(stav.energy_allmin / stav.pocethracov) 'calculate  players energy
  545.             IF stav.energy_player_start < stav.energy_playermin THEN stav.energy_player_start = stav.energy_playermin
  546.             IF stav.energy_player_start > stav.energy_playermax THEN stav.energy_player_start = stav.energy_playermax
  547.         END IF
  548.  
  549.         IF game.isnetgame = 1 AND game.clienth <> 0 AND game.clientid > 0 AND stav.gameplay = 1 THEN game.start = 1
  550.         IF game.isnetgame = 1 THEN SyncNetGameData
  551.         _DISPLAY
  552.         _LIMIT stav.fps
  553.  
  554.     LOOP UNTIL game.start = 1 OR game.exituj = 1
  555.     game.start = 0
  556.     IF game.clientid > 0 THEN hrac(game.clientid).isready = 0
  557.     '--------end of main menu---------------------------------------------------------------------------------------------------------------------------------------------------------------------
  558.  
  559.     '----------------------------------new game start-------------------------------------------------------------------------------------------------
  560.     WINDOW (0, 0)-(stav.maxx, stav.maxy)
  561.     IF game.exituj <> 1 THEN 'new game starts here
  562.         '----------------------------------set the world on the new game start-------------------------------
  563.         IF game.hosth <> 0 THEN AddMessage "The match has been started by the host. Whoever scores" + RTRIM$(STR$(stav.winscore)) + " times wins.", MSG_COL, MSG_DUR_STD, 1
  564.  
  565.         FOR i = 1 TO PLAYER_NET_MAX
  566.             FOR j = 1 TO PLAYER_NET_MAX
  567.                 stats(i, j) = 0
  568.             NEXT j
  569.         NEXT i
  570.  
  571.         stav.gameplay = 1
  572.         stav.pocetgul = stav.pocetgulzvoleny + stav.pocethracov
  573.         stav.pocetgulx = MATHROUND(SQR(4 / 3 * stav.pocetgul))
  574.         IF stav.pocetgulx < stav.pocethracov THEN stav.pocetgulx = stav.pocethracov
  575.         stav.pocetguly = _CEIL(stav.pocetgul / stav.pocetgulx)
  576.  
  577.         IF stav.pocethracov < 2 THEN stav.pocethracov = 2
  578.         FOR i = 1 TO stav.pocethracov
  579.             hrac(i).skore = 0
  580.         NEXT i
  581.  
  582.         FOR n = 1 TO stav.pocethracov: bal(n).syncstr = GETITEM_PBAL: bal(n).synctime = 0: NEXT n
  583.         FOR n = stav.pocethracov + 1 TO UBOUND(bal): bal(n).syncstr = GETITEM_BAL: bal(n).synctime = 0: NEXT n
  584.  
  585.         stav.zacinabal = hrac(INT(RND * stav.pocethracov + 1)).bal
  586.  
  587.         DO 'new round starts here
  588.             '-------------------------------set the world on the new round start---------------------------
  589.             stav.koniec_kola = 0
  590.             stav.pauza = 0
  591.             stav.vitaz = 0
  592.             game.exituj = 0
  593.             stav.baljeto = 0
  594.  
  595.             IF game.isnetgame = 1 AND game.hosth <> 0 THEN 'engage new spectating players
  596.                 FOR i = 2 TO stav.pocethracov
  597.                     IF client(i).spectating = 1 THEN
  598.                         hrac(i).skore = 0
  599.                         bal(hrac(i).bal).hrac = i
  600.                         bal(hrac(i).bal).complayer = 0
  601.                         bal(hrac(i).bal).riadena = 1
  602.                         client(i).spectating = 0
  603.                         bal(i).vi.x = 0: bal(i).vi.y = 0
  604.                         FOR j = 1 TO PLAYER_NET_MAX
  605.                             stats(i, j) = 0
  606.                             stats(j, i) = 0
  607.                         NEXT j
  608.                     END IF
  609.                 NEXT i
  610.             END IF
  611.  
  612.             RANDOMIZE TIMER
  613.             coloffset = 104 + INT(RND * 10)
  614.             colcoef = (1 + INT(RND * (246 - coloffset))) / stav.pocetgulx
  615.             FOR i = 1 TO stav.pocetguly + 1 'set all balls
  616.                 FOR j = 1 TO stav.pocetgulx
  617.                     n = (i - 1) * stav.pocetgulx + j:
  618.                     IF n <= stav.pocetgul THEN
  619.                         ry = 20 + RND * INT(((stav.maxy - 100) / (stav.pocetguly + 1) - 25) / 2)
  620.                         rx = 10 + RND * INT(((stav.maxx - 100) / (stav.pocetgulx + 1) - 25) / 2)
  621.                         bcol = coloffset + j * colcoef
  622.                         bal(n).i = n
  623.                         bal(n).col = bcol
  624.                         bal(n).x = 100 + j * INT((stav.maxx - 100) / (stav.pocetgulx + 1))
  625.                         bal(n).y = 100 + i * INT((stav.maxy - 100) / (stav.pocetguly + 1))
  626.                         bal(n).r = rx * -(rx < ry) + ry * -(ry < rx)
  627.                         bal(n).m = 4 / 4 * pi * bal(n).r ^ 2
  628.                         bal(n).v.x = -10 / FPS: bal(n).v.y = -30 / FPS
  629.                         bal(n).a.x = ACC_X * stav.maxx / RES_X_TARG
  630.                         bal(n).a.y = ACC_Y * stav.maxx / RES_X_TARG
  631.                         bal(n).vi.x = -(bal(n).complayer = 1) * RND * stav.vmax - stav.vmax / 2
  632.                         bal(n).vi.y = -(bal(n).complayer = 1) * RND * stav.vmax - stav.vmax / 2
  633.                         bal(n).riadena = 0
  634.                         bal(n).energia = stav.energy_player_start
  635.                         bal(n).freezedtime = 0: bal(n).klucka_time = 0: bal(n).klucka_pocet = 0
  636.                         bal(n).klucka_trvanie = 0: bal(n).klucka_uhol = 0
  637.                         bal(n).boost_time = 0: bal(n).boost_trvanie = 0
  638.                         bal(n).synctime = 0
  639.                     END IF
  640.                 NEXT j
  641.             NEXT i
  642.  
  643.             '---set players balls default atributtes--------------------------------------------------------
  644.             FOR i = 1 TO stav.pocethracov
  645.                 hrac(i).bal = i
  646.                 bal(hrac(i).bal).hrac = i
  647.                 bal(hrac(i).bal).riadena = 1
  648.                 IF stav.AIallowed = 0 AND klav(hrac(i).klavesy).typ = 4 THEN
  649.                     bal(hrac(i).bal).energia = 0
  650.                     bal(hrac(i).bal).riadena = 0
  651.                 ELSE
  652.                     bal(hrac(i).bal).r = PLAY_BALL_R * stav.maxx / RES_X_TARG
  653.                     bal(hrac(i).bal).m = 4 / 4 * pi * PLAY_BALL_R ^ 2 / 2 'Players have only half density
  654.                     bal(hrac(i).bal).col = hrac(i).col
  655.                     bal(hrac(i).bal).strat = 3
  656.                 END IF
  657.             NEXT i
  658.  
  659.             '---decide initially tagged player-------------------------------------------------------------
  660.             zacinalNaposledyBal = stav.zacinabal
  661.             stav.balbudeto = stav.zacinabal
  662.             DO
  663.                 stav.zacinabal = stav.zacinabal + 1
  664.                 IF stav.zacinabal > stav.pocetgulx THEN stav.zacinabal = 1
  665.             LOOP UNTIL bal(stav.zacinabal).riadena = 1
  666.             stav.baljeto = stav.zacinabal
  667.  
  668.             FOR b = 1 TO stav.pocethracov
  669.                 bal(hrac(b).bal).protivnik = stav.baljeto * -(hrac(b).bal <> stav.baljeto)
  670.             NEXT b
  671.             bal(stav.zacinabal).protivnik = zacinalNaposledyBal
  672.  
  673.             '----------------------------------game action loop--------------------------------------------
  674.             DO
  675.                 game.tstart = TIMER(.001)
  676.                 _LIMIT stav.fps
  677.                 CLS 1
  678.                 k = _KEYHIT
  679.                 IF k = 116 AND game.clientid > 0 AND game.msgwrite = 0 THEN game.msgwrite = 1: k = 0 'message writing mode
  680.                 IF game.msgwrite = 1 THEN WriteMessage k: k = 0
  681.                 IF NOT (game.isnetgame = 1 AND game.clienth <> 0) AND (k = 112 OR k = 102) THEN stav.pauza = 1 - stav.pauza
  682.  
  683.                 DrawScore 'Draw players score on screen
  684.                 DisplayMessages 'Draw messages
  685.  
  686.                 '---debug code begin---------------------------------------------
  687.                 IF k = 98 THEN game.printdebug = -(game.printdebug = 0)
  688.                 IF game.printdebug = 1 THEN DebugScreen k 'display debug info on screen
  689.                 '---debug code end-----------------------------------------------
  690.  
  691.                 IF k = 99 THEN game.show_stats = -(game.show_stats = 0)
  692.                 IF game.show_stats = 1 THEN DisplayStatistics 'display match statistics
  693.  
  694.                 IF stav.pauza = 0 THEN
  695.                     FOR i = 1 TO stav.pocethracov
  696.                         ResolvePlayerControl bal(hrac(i).bal), klav(hrac(i).klavesy) 'resolve players control
  697.                         IF bal(hrac(i).bal).riadena = 1 THEN
  698.                             CalculatePlayerState bal(hrac(i).bal) 'calculate player status
  699.                         END IF
  700.                     NEXT i
  701.  
  702.                     FOR n = 1 TO stav.pocetgul
  703.                         PerformMovement bal(n) 'do ball movement
  704.                     NEXT n
  705.  
  706.                     FOR i = 1 TO stav.pocetgul
  707.                         FOR j = i + 1 TO stav.pocetgul
  708.                             BounceTest bal(i), bal(j) 'Test balls bounce
  709.                         NEXT j
  710.                     NEXT i
  711.                 ELSE
  712.                     PauseGame
  713.                 END IF
  714.  
  715.                 IF game.isnetgame = 1 THEN SyncNetGameData
  716.  
  717.                 FOR n = 1 TO stav.pocetgul
  718.                     DrawBall bal(n) 'draw ball
  719.                 NEXT n
  720.  
  721.                 IF stav.vitaz <> 0 THEN DrawWinScreen
  722.  
  723.                 IF NOT (game.isnetgame = 1 AND game.clienth <> 0) AND (k = 102) THEN stav.pauza = 1
  724.                 IF k = 27 AND game.isnetgame = 1 THEN AddMessage "To quit network game press 't' and type '/q'", MSG_COL, MSG_DUR_STD, 0: k = 0
  725.                 IF k = 27 AND game.isnetgame = 0 THEN stav.koniec_kola = 2
  726.  
  727.                 _DISPLAY 'Turn video page over
  728.                 game.tend = TIMER(.001)
  729.                 CalculateFps
  730.             LOOP UNTIL stav.koniec_kola >= 1
  731.             '---------------------------end of game action loop---------------------------------------------
  732.  
  733.             IF stav.vitaz <> 0 THEN k = 0
  734.             anyHumanPlayer = HumanPlayersCount
  735.             resumegame = 0
  736.             DO 'wait for resume if not net game
  737.                 _LIMIT stav.fps
  738.                 IF k = 27 AND game.hosth = 0 THEN stav.koniec_kola = 2
  739.                 FOR i = 1 TO stav.pocethracov
  740.                     IF klav(hrac(i).klavesy).typ = 3 THEN ReadGamepad klav(hrac(i).klavesy)
  741.                     IF klav(hrac(i).klavesy).isshoota THEN resumegame = 1
  742.                     IF klav(hrac(i).klavesy).isshootb THEN stav.koniec_kola = 2
  743.                     IF klav(hrac(i).klavesy).typ = 1 THEN IF _KEYDOWN(klav(hrac(i).klavesy).shoota) THEN resumegame = 1
  744.                 NEXT i
  745.                 k = _KEYHIT
  746.             LOOP UNTIL (resumegame = 1) OR (anyHumanPlayer = 0) OR (stav.koniec_kola = 2) OR (game.isnetgame = 1)
  747.             '---------------------------end of round-----------------------------------------------------------
  748.         LOOP UNTIL stav.koniec_kola = 2 OR (stav.AIallowed = 0 AND anyHumanPlayer < 2)
  749.         IF (stav.AIallowed = 0 AND anyHumanPlayer < 2) THEN AddMessage "Match was ended for less than 2 players left", MSG_COL, MSG_DUR_STD, 0
  750.     END IF
  751.     '---------------------------end of game------------------------------------------------------------
  752.     game.autostart_time = TIMER + stav.autostart_set
  753. LOOP UNTIL game.exituj = 1
  754. CLS 1
  755. '---------------------------end of program------------------------------------------------------------
  756.  
  757. '////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  758. '----------------------------------------------------------------------------------------------------------------------------------------------------
  759. SUB DrawWinScreen
  760.     DIM statstab(1 TO PLAYER_NET_MAX) AS STRING
  761.     IF stav.vitaz > 0 THEN
  762.         IF hrac(stav.vitaz).skore >= stav.winscore THEN windelay = DELAY_MATCHEND ELSE windelay = DELAY_ROUNDSTART
  763.         IF game.autostart_time - windelay > TIMER THEN game.autostart_time = game.autostart_time - 86399
  764.         IF (game.autostart_time <= TIMER) AND (game.isnetgame = 0 OR game.hosth <> 0) THEN
  765.             stav.koniec_kola = 1 'end of round
  766.             IF hrac(stav.vitaz).skore >= stav.winscore THEN stav.koniec_kola = 2 'end of match either
  767.         END IF
  768.         IF (game.isnetgame = 0 OR game.hosth <> 0) THEN stav.autostart_timeleft = game.autostart_time - TIMER
  769.         IF INT(stav.autostart_timeleft) >= 0 THEN timeleft$ = "  (" + LTRIM$(STR$(INT(stav.autostart_timeleft))) + ")" ELSE timeleft$ = ""
  770.         IF hrac(stav.vitaz).skore < stav.winscore THEN
  771.             winstr1$ = "....and the winner of this round is... " + RTRIM$(hrac(stav.vitaz).meno) + timeleft$
  772.             winstr2$ = "Press <fire> for the next round"
  773.         ELSE
  774.             winstr1$ = "...>>> " + RTRIM$(hrac(stav.vitaz).meno) + " <<<.... scores " + LTRIM$(STR$(stav.winscore)) + " and becomes the MATCH WINNER!" + timeleft$
  775.             winstr2$ = "Press <fire> to return to the menu"
  776.             DisplayStatistics
  777.         END IF
  778.         COLOR bal(hrac(stav.vitaz).bal).col
  779.         _PRINTSTRING ((game.maxx - LEN(winstr1$) * 10) / 2, (game.maxy - 50) / 2), winstr1$
  780.         IF stav.koniec_kola > 0 THEN _PRINTSTRING ((game.maxx - LEN(winstr2$) * 10) / 2, (game.maxy - 50) / 2 + 50), winstr2$
  781.     END IF
  782.  
  783. '----------------------------------------------------------------------------------------------------------------------------------------------------
  784. SUB DisplayStatistics
  785.     IF stav.pocethracov <= PLAYER_NET_MAX THEN playercount = stav.pocethracov ELSE playercount = PLAYER_NET_MAX
  786.     row = INT((game.maxy - (playercount + 4) * _FONTHEIGHT) / _FONTHEIGHT)
  787.     col = INT((game.maxx - ((playercount + 2) * 11 * _FONTWIDTH)) / 2 / _FONTWIDTH)
  788.     IF row < 1 THEN row = 1
  789.     IF col < 1 THEN col = 1
  790.     COLOR MSG_COL
  791.     LOCATE row, col
  792.     PRINT "Match statistics - tags given by player"
  793.     LOCATE row + 1, col
  794.     PRINT STRING$((playercount + 2) * 11, "-"),
  795.     LOCATE row + 2, col
  796.     PRINT "Player"
  797.     FOR i = 1 TO playercount
  798.         COLOR hrac(i).col
  799.         LOCATE row + 2, col + i * 11
  800.         PRINT RTRIM$(hrac(i).meno)
  801.     NEXT i
  802.     COLOR MSG_COL
  803.     LOCATE row + 2, col + i * 11
  804.     PRINT "Tags given"
  805.     FOR i = 1 TO playercount
  806.         tagsum = 0
  807.         LOCATE row + 2 + i, col
  808.         COLOR hrac(i).col
  809.         PRINT RTRIM$(hrac(i).meno)
  810.         FOR j = 1 TO playercount
  811.             LOCATE row + 2 + i, col + j * 11
  812.             IF stats(i, j) > 0 THEN PRINT STR$(stats(i, j))
  813.             tagsum = tagsum + stats(i, j)
  814.         NEXT j
  815.         LOCATE row + 2 + i, col + j * 11
  816.         PRINT STR$(tagsum)
  817.     NEXT i
  818.  
  819. '----------------------------------------------------------------------------------------------------------------------------------------------------
  820. FUNCTION HumanPlayersCount 'set pre-game parameters
  821.     HumanPlayersCount = 0
  822.     FOR i = 1 TO stav.pocethracov
  823.         IF bal(hrac(i).bal).complayer = 0 THEN HumanPlayersCount = HumanPlayersCount + 1
  824.     NEXT i
  825.  
  826. '----------------------------------------------------------------------------------------------------------------------------------------------------
  827. SUB SetGlobalParameters 'set pre-game parameters
  828.     '-------------------------------set game session parameters----------------------------------------------------------------------------------------
  829.     game.fpsactual = FPS
  830.     game.isnetgame = 0
  831.     game.clienth = 0
  832.     game.clientid = 0
  833.     game.hosth = 0
  834.     game.host_port = HOSTPORT_DEFAULT
  835.     game.host_ip = HOSTIP_DEFAULT
  836.     game.printdebug = DEBUGMODE
  837.     game.exituj = 0
  838.     game.start = 0
  839.     game.newmsg = 0
  840.     IF RES_X = -1 THEN game.maxx = _DESKTOPWIDTH ELSE game.maxx = RES_X
  841.     IF RES_Y = -1 THEN game.maxy = _DESKTOPHEIGHT ELSE game.maxy = RES_Y
  842.     game.msgcount = 0
  843.     game.msgwrite = 0
  844.     game.msgtext = ""
  845.     game.show_stats = 0
  846.     sync.syncstr = GETITEM_SYNC
  847.     sync.synctime = 0
  848.  
  849.     '-------------------------------set game world parameters----------------------------------------------------------------------------------------
  850.     pi = _PI
  851.     stav.syncstr = GETITEM_STAV
  852.     stav.synctime = 0
  853.     stav.pauza = 0
  854.     stav.gameplay = 0
  855.     stav.fps = FPS
  856.     stav.pocethracov = PLAYER_COUNT_INI
  857.     stav.pocetgulzvoleny = POCET_GUL_INI
  858.     stav.pocetgul = POCET_GUL_INI
  859.     stav.energy_allmin = ENERGY_ALL_MIN
  860.     stav.energy_playermin = ENERGY_PLAYER_MIN
  861.     stav.energy_playermax = ENERGY_PLAYER_MAX
  862.     stav.energydec = ENERGY_DEC
  863.     stav.odpor = FRICTION
  864.     stav.minx = 0
  865.     stav.miny = 0
  866.     stav.maxx = RES_X_TARG
  867.     stav.maxy = RES_Y_TARG
  868.     stav.vmax = V_MAX * stav.maxx / RES_X_TARG
  869.     stav.maxcol = MAX_COL
  870.     stav.boostallowed = 1
  871.     stav.massallowed = 0 'sticky balls problem if mass allowed unsolved yet
  872.     stav.autostart_set = 0
  873.     game.autostart_time = 0
  874.     stav.AIallowed = 1
  875.     stav.winscore = 3
  876.     stav.energy_player_start = (stav.energy_allmin / stav.pocethracov) 'calculate  players energy
  877.     IF stav.energy_player_start < stav.energy_playermin THEN stav.energy_player_start = stav.energy_playermin
  878.     IF stav.energy_player_start > stav.energy_playermax THEN stav.energy_player_start = stav.energy_playermax
  879.  
  880.     stats(PLAYER_NET_MAX + 1, 1) = ASC(GETITEM_STAT, 1)
  881.     stats(PLAYER_NET_MAX + 1, 2) = ASC(GETITEM_STAT, 2)
  882.     stats(PLAYER_NET_MAX + 1, 3) = ASC(GETITEM_STAT, 3)
  883.  
  884.     '-------------------------------set game controllers---------------------------------------------------------------------------------------------
  885.     FOR i = 1 TO UBOUND(klav) 'set all the other game controllers as unused, few reserved for net players
  886.         IF i <= PLAYER_NET_MAX THEN
  887.             klav(i).typ = 5
  888.             klav(i).nazov = "NetControl" + LTRIM$(STR$(i))
  889.         ELSE
  890.             klav(i).typ = 0
  891.         END IF
  892.         klav(i).devid = 0
  893.         klav(i).syncstr = GETITEM_KLAV
  894.         klav(i).synctime = 0
  895.     NEXT i
  896.  
  897.     'set default controllers
  898.     kid = PLAYER_NET_MAX + 1 'CPU void controller
  899.     klav(kid).id = kid: klav(kid).devid = 1: klav(kid).nazov = "<CPU>": klav(kid).typ = 4
  900.  
  901.     kid = PLAYER_NET_MAX + 2 'keyboard set 1 controller
  902.     klav(kid).id = kid: klav(kid).devid = 1: klav(kid).nazov = "<Arrow keys + LShift>": klav(kid).typ = 1
  903.     klav(kid).up = 18432: klav(kid).down = 20480: klav(kid).left = 19200: klav(kid).right = 19712
  904.     klav(kid).shoota = 100304: klav(kid).shootb = 100306
  905.  
  906.     kid = PLAYER_NET_MAX + 3 'keyboard set 2 controller
  907.     klav(kid).id = kid: klav(kid).devid = 1: klav(kid).nazov = "<W,A,S,D + RShift>": klav(kid).typ = 1
  908.     klav(kid).up = 119: klav(kid).down = 115: klav(kid).left = 97: klav(kid).right = 100
  909.     klav(kid).shoota = 100303: klav(kid).shootb = 100305
  910.  
  911.     kid = PLAYER_NET_MAX + 4 'keyboard set 3 controller
  912.     klav(kid).id = kid: klav(kid).devid = 1: klav(kid).nazov = "<I,J,K,L + Enter>": klav(kid).typ = 1
  913.     klav(kid).up = 105: klav(kid).down = 107: klav(kid).left = 106: klav(kid).right = 108
  914.     klav(kid).shoota = 13: klav(kid).shootb = 21248
  915.  
  916.     '-------------------------------set players------------------------------------------------------------------------------------------------------
  917.     FOR i = 1 TO UBOUND(hrac) 'initial setting for all players
  918.         hrac(i).bal = i
  919.         hrac(i).meno = "Player" + LTRIM$(STR$(i))
  920.         hrac(i).skore = 0
  921.         hrac(i).klavesy = CPU_CONTROLID
  922.         hrac(i).col = 32 + i * 3
  923.         hrac(i).clientid = 0
  924.         bal(hrac(i).bal).complayer = 1
  925.         hrac(i).syncstr = GETITEM_HRAC
  926.         hrac(i).synctime = 0
  927.     NEXT i
  928.  
  929.     'personalize some players initially
  930.     bal(hrac(1).bal).complayer = 0
  931.     hrac(1).klavesy = PLAYER_NET_MAX + 2
  932.     hrac(1).meno = "Chuck"
  933.     hrac(2).meno = "Arnold"
  934.     hrac(3).meno = "Jean"
  935.     hrac(4).meno = "Bruce"
  936.     hrac(5).meno = "Dolph"
  937.     hrac(6).meno = "Sylvester"
  938.     hrac(7).meno = "Jet"
  939.     hrac(8).meno = "Dwayne"
  940.  
  941.     '-------------------------------set behaviour strategies------------------------------------------------------------------------------------------
  942.     'some void strategy
  943.     strat(1).okraj = PRIESTOR_MIN:
  944.     strat(1).okraj_rot = 0.1
  945.     strat(1).chybasmeru = 0.00:
  946.     strat(1).klucka_avgtm = 1 * FPS: strat(1).klucka_pocet = 5
  947.     strat(1).klucka_trvanie = 0.25 * FPS: strat(1).klucka_uhol = pi / 2: strat(1).klucka_uholodch = 0.2
  948.     strat(1).detekcia_sin = SIN(pi / 4): strat(1).detekcia_vzdfull = 500: strat(1).detekcia_vzdmin = 1000: strat(1).detekcia_rot = 0.5
  949.     strat(1).boost_dist = 200
  950.     strat(1).boost_chasesin = SIN(pi / 8)
  951.     strat(1).mate_cosenmax = -0.8 'cos(140/180*pi)
  952.     strat(1).mate_rot = 0.1 ' 3*pi /FPS
  953.     strat(1).mate_en_distmin = 5 * PLAY_BALL_R
  954.     strat(1).mate_distmin = 4 * PLAY_BALL_R
  955.  
  956.     'non dodging strategy
  957.     strat(2).okraj = PRIESTOR_MIN + 10
  958.     strat(2).okraj_rot = 0.1
  959.     strat(2).chybasmeru = 0.00
  960.     strat(2).reactdrop_per = 0 * FPS
  961.     strat(2).reactdrop_dur = 0 * FPS
  962.     strat(2).klucka_avgtm = 10 * FPS
  963.     strat(2).klucka_pocet = 0
  964.     strat(2).klucka_trvanie = 0 * FPS
  965.     strat(2).klucka_uhol = 0 * pi / 3
  966.     strat(2).klucka_uholodch = 0.2
  967.     strat(2).detekcia_sin = SIN(pi / 6)
  968.     strat(2).detekcia_vzdfull = 500
  969.     strat(2).detekcia_vzdmin = 1000
  970.     strat(2).detekcia_rot = 0.5
  971.     strat(2).boost_dist = 200
  972.     strat(2).boost_chasesin = SIN(pi / 8)
  973.     strat(2).mate_cosenmax = -0.8 'cos(140/180*pi)
  974.     strat(2).mate_rot = 0.1 ' 3*pi /FPS
  975.     strat(2).mate_en_distmin = 5 * PLAY_BALL_R
  976.     strat(2).mate_distmin = 4 * PLAY_BALL_R
  977.  
  978.     'dodging strategy - currently used for all AI players
  979.     strat(3).okraj = PRIESTOR_MIN + 10
  980.     strat(3).okraj_rot = 0.1
  981.     strat(3).chybasmeru = 0.30
  982.     strat(3).reactdrop_per = 0.8 * FPS
  983.     strat(3).reactdrop_dur = 0.4 * FPS
  984.     strat(3).klucka_avgtm = 5 * FPS
  985.     strat(3).klucka_pocet = 3
  986.     strat(3).klucka_trvanie = 2 * FPS
  987.     strat(3).klucka_uhol = pi * 2 / 3
  988.     strat(3).klucka_uholodch = 0.2
  989.     strat(3).detekcia_sin = SIN(pi / 6)
  990.     strat(3).detekcia_vzdfull = 300
  991.     strat(3).detekcia_vzdmin = 1500
  992.     strat(3).detekcia_rot = 0.5 '10 * pi / FPS
  993.     strat(3).boost_dist = 200
  994.     strat(3).boost_chasesin = SIN(pi / 8)
  995.     strat(3).mate_cosenmax = -0.8 'cos(140/180*pi)
  996.     strat(3).mate_rot = 0.1 ' 3*pi /FPS
  997.     strat(3).mate_en_distmin = 8 * PLAY_BALL_R
  998.     strat(3).mate_distmin = 4 * PLAY_BALL_R
  999.     strat(3).adrenalin_rate = 0.90
  1000.     strat(3).adrenalin_energy = 0.30
  1001.  
  1002.     '-------------------------------set main menu-----------------------------------------------------------------------------------------------------
  1003.     menu.item_active = 1
  1004.     menu.items_count = 22
  1005.     menu.cursor_col = 15
  1006.     menu.cursor_width = 400
  1007.     menu.item_height = FONT_H + 4
  1008.     menu.cursor_xpos = INT((game.maxx - menu.cursor_width) / 2)
  1009.     menu.ypos = INT((game.maxy - menu.items_count * menu.item_height) / 2)
  1010.     menu.cursor_out = 5
  1011.     '-------------------------------set balls---------------------------------------------------------------------------------------------------------
  1012.     FOR n = 1 TO stav.pocethracov: bal(n).syncstr = GETITEM_PBAL: bal(n).synctime = 0: NEXT n
  1013.     FOR n = stav.pocethracov + 1 TO UBOUND(bal): bal(n).syncstr = GETITEM_BAL: bal(n).synctime = 0: NEXT n
  1014.  
  1015. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1016. SUB WriteMessage (k AS INTEGER)
  1017.     DIM tmptext AS STRING
  1018.     IF game.clientid > 0 THEN
  1019.         msginput_x = 10
  1020.         msginput_y = RES_Y - 20
  1021.  
  1022.         tmptext = LEFT$(game.msgtext, LEN(RTRIM$(game.msgtext)) - 1)
  1023.         IF k >= 32 AND k <= 126 AND LEN(tmptext) < MSG_LEN_MAX THEN tmptext = tmptext + CHR$(k)
  1024.         IF k = 8 AND LEN(tmptext) > 0 THEN tmptext = LEFT$(tmptext, LEN(tmptext) - 1)
  1025.         IF k = 27 THEN game.msgwrite = 0
  1026.  
  1027.         COLOR MSG_COL
  1028.         _PRINTSTRING (msginput_x, msginput_y), "Type message>>  " + tmptext
  1029.         IF k = -1 AND game.msgwrite = 0 THEN _PRINTSTRING (msginput_x, msginput_y), "Press 't' to writte text message to other players" + tmptext
  1030.  
  1031.  
  1032.         IF k = 13 THEN
  1033.             IF LEFT$(tmptext, 1) = "/" THEN
  1034.                 AddMessage tmptext, MSG_COL, MSG_DUR_STD, 0
  1035.             ELSE
  1036.                 AddMessage RTRIM$(hrac(game.clientid).meno) + "> " + tmptext, hrac(game.clientid).col, MSG_DUR_STD, 1
  1037.             END IF
  1038.             game.msgtext = CHR$(92)
  1039.             game.msgwrite = 0
  1040.         ELSE
  1041.             game.msgtext = tmptext + CHR$(92)
  1042.         END IF
  1043.     END IF
  1044.  
  1045. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1046. SUB SetUpControllers 'detect gamepads and set them as game controllers
  1047.     DIM klav_volne AS INTEGER, devices AS INTEGER
  1048.     DIM dev AS STRING
  1049.  
  1050.     FOR i = 1 TO UBOUND(klav) 'dispose all gamepad constrollers
  1051.         IF klav(i).typ = 3 THEN
  1052.             klav(i).typ = 0
  1053.             klav(i).devid = 0
  1054.         END IF
  1055.     NEXT i
  1056.     klav_volne = 1
  1057.     game.pocetgamepadov = 0
  1058.     DO
  1059.         klav_volne = klav_volne + 1
  1060.     LOOP UNTIL klav(klav_volne).typ = 0 OR klav_volne = UBOUND(klav)
  1061.     klav_used = klav_volne - 1
  1062.     devices = _DEVICES 'must be read prior to other device functions usage
  1063.     FOR i = 1 TO devices
  1064.         dev = _DEVICE$(i)
  1065.         IF INSTR(dev, "[CONTROLLER]") > 0 AND INSTR(dev, "[AXIS]") > 0 AND klav_volne <= UBOUND(klav) THEN 'set new controller if gamepad
  1066.             klav(klav_volne).id = klav_volne
  1067.             klav(klav_volne).devid = i
  1068.             klav(klav_volne).typ = 3
  1069.             klav(klav_volne).nazov = "<Gamepad" + LTRIM$(STR$(klav_volne - klav_used)) + ">"
  1070.             klav(klav_volne).xaxis = 1
  1071.             klav(klav_volne).yaxis = 2
  1072.             klav(klav_volne).dxaxis = _LASTAXIS(i) - 1
  1073.             klav(klav_volne).dyaxis = _LASTAXIS(i)
  1074.             klav(klav_volne).shoota = 1
  1075.             klav(klav_volne).shootb = 8
  1076.             klav_volne = klav_volne + 1
  1077.             game.pocetgamepadov = game.pocetgamepadov + 1
  1078.         END IF
  1079.     NEXT i
  1080.     game.pocetkontrolerov = klav_volne - 1
  1081.  
  1082. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1083. SUB MainMenuScreen 'perform choises in main menu
  1084.  
  1085. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1086. SUB SyncNetGameData
  1087.     DIM i AS INTEGER, isync AS tsyncvar, clklav AS tklavesy, clhrac AS thrac, imsg AS tmessage
  1088.     DIM sync_stav AS INTEGER, sync_pbal AS INTEGER, sync_bal AS INTEGER, sync_hrac AS INTEGER
  1089.     IF game.isnetgame = 1 THEN
  1090.         '--------------------------host side---------------------------------------------------------------------------------------------------------
  1091.         IF game.hosth <> 0 THEN 'sync host data
  1092.             FOR i = PLAYER_NET_MIN TO PLAYER_NET_MAX 'for each client try to connect
  1093.                 IF client(i).conn = 0 AND i <= stav.pocethracov THEN
  1094.                     client(i).conn = _OPENCONNECTION(game.hosth) 'try to open connection if not open
  1095.                     client(i).get_item = GETITEM_SYNC
  1096.                     client(i).get_isdesync = 0
  1097.                     client(i).get_time = TIMER(.001)
  1098.                     IF client(i).conn <> 0 THEN 'if connection opened
  1099.                         client(i).ip = _CONNECTIONADDRESS(client(i).conn)
  1100.                         PLAY "MBL64O2CEG>C"
  1101.                         IF stav.gameplay = 1 THEN
  1102.                             client(i).spectating = 1
  1103.                             game.sync_stats = 1
  1104.                             msgtext$ = " joins the game from the next round"
  1105.                         ELSE
  1106.                             client(i).spectating = 0
  1107.                             msgtext$ = " has joined the game"
  1108.                         END IF
  1109.                         AddMessage "Player " + RTRIM$(hrac(i).meno) + msgtext$, MSG_COL, MSG_DUR_STD, 1
  1110.                     END IF
  1111.                 END IF
  1112.                 IF (TIMER(.001) - client(i).get_time) > NET_TIMEOUT_LIMIT AND client(i).conn <> 0 THEN CloseClientConn (i)
  1113.             NEXT i
  1114.             FOR i = PLAYER_NET_MIN TO PLAYER_NET_MAX 'sync data with each client
  1115.                 sync.clientid = i
  1116.                 IF client(i).conn <> 0 THEN
  1117.                     DO
  1118.                         IF client(i).get_isdesync = 1 THEN 'flush acquired data if desynced
  1119.                             GET #client(i).conn, , flush$
  1120.                             client(i).get_isdesync = 0
  1121.                             client(i).get_item = GETITEM_SYNC
  1122.                         END IF
  1123.  
  1124.                         IF client(i).get_item = GETITEM_SYNC THEN 'first get sync info on what item to get next
  1125.                             GET #client(i).conn, , isync
  1126.                             IF NOT EOF(client(i).conn) THEN
  1127.                                 IF isync.syncstr = GETITEM_SYNC THEN
  1128.                                     client(i).get_item = isync.itemstr
  1129.                                     client(i).get_time = TIMER(.001)
  1130.                                     client(i).isdesynced = isync.isdesync
  1131.                                 ELSE
  1132.                                     client(i).get_isdesync = 1
  1133.                                 END IF
  1134.                             END IF
  1135.                         END IF
  1136.  
  1137.                         IF client(i).get_item = GETITEM_KLAV THEN 'get controller state from client
  1138.                             GET #client(i).conn, , clklav
  1139.                             IF NOT EOF(client(i).conn) THEN
  1140.                                 IF clklav.syncstr = GETITEM_KLAV THEN
  1141.                                     klav(hrac(i).klavesy).isup = clklav.isup
  1142.                                     klav(hrac(i).klavesy).isdown = clklav.isdown
  1143.                                     klav(hrac(i).klavesy).isright = clklav.isright
  1144.                                     klav(hrac(i).klavesy).isleft = clklav.isleft
  1145.                                     klav(hrac(i).klavesy).isshoota = clklav.isshoota
  1146.                                     klav(hrac(i).klavesy).isshootb = clklav.isshootb
  1147.                                     klav(hrac(i).klavesy).gvektor = clklav.gvektor
  1148.                                     client(i).get_time = TIMER(.001)
  1149.                                 ELSE
  1150.                                     client(i).get_isdesync = 1
  1151.                                 END IF
  1152.                                 client(i).get_item = GETITEM_SYNC
  1153.                             END IF
  1154.                         END IF
  1155.  
  1156.                         IF client(i).get_item = GETITEM_HRAC THEN 'get client's player
  1157.                             GET #client(i).conn, , clhrac
  1158.                             IF NOT EOF(client(i).conn) THEN
  1159.                                 IF clhrac.syncstr = GETITEM_HRAC THEN
  1160.                                     clhrac.skore = hrac(i).skore 'keep players socre
  1161.                                     hrac(i) = clhrac 'get player data from client
  1162.                                     hrac(i).klavesy = i 'set player net controlled
  1163.                                     hrac(i).clientid = i 'set player's clientid
  1164.                                     client(i).get_time = TIMER(.001)
  1165.                                 ELSE
  1166.                                     client(i).get_isdesync = 1
  1167.                                 END IF
  1168.                                 client(i).get_item = GETITEM_SYNC
  1169.                             END IF
  1170.                         END IF
  1171.  
  1172.                         IF client(i).get_item = GETITEM_MESG THEN 'get message from client
  1173.                             GET #client(i).conn, , imsg
  1174.                             IF NOT EOF(client(i).conn) THEN
  1175.                                 IF imsg.syncstr = GETITEM_MESG THEN
  1176.                                     AddMessage imsg.text, imsg.col, imsg.time_dur, 1
  1177.                                     msg(game.msgcount).clientid = imsg.clientid
  1178.                                     client(i).get_time = TIMER(.001)
  1179.                                 ELSE
  1180.                                     client(i).get_isdesync = 1
  1181.                                 END IF
  1182.                                 client(i).get_item = GETITEM_SYNC
  1183.                             END IF
  1184.                         END IF
  1185.  
  1186.                     LOOP UNTIL EOF(client(i).conn)
  1187.                 END IF
  1188.             NEXT i
  1189.  
  1190.             IF TIMER(.001) - stav.synctime > UPDATE_PER_STAV THEN 'check if update time
  1191.                 sync_stav = 1
  1192.             ELSE sync_stav = 0
  1193.             END IF
  1194.             IF TIMER(.001) - bal(1).synctime > UPDATE_PER_PLBAL THEN
  1195.                 FOR j = 1 TO stav.pocethracov: ipbal(j) = bal(j): NEXT j 'fill transfer array
  1196.                 sync_pbal = 1
  1197.             ELSE sync_pbal = 0
  1198.             END IF
  1199.             IF TIMER(.001) - bal(stav.pocethracov + 1).synctime > UPDATE_PER_BAL THEN
  1200.                 FOR j = 1 TO stav.pocetgulzvoleny: ibal(j) = bal(j + stav.pocethracov): NEXT j 'fill transfer array
  1201.                 sync_bal = 1
  1202.             ELSE sync_bal = 0
  1203.             END IF
  1204.             IF TIMER(.001) - hrac(1).synctime > UPDATE_PER_HRAC THEN
  1205.                 sync_hrac = 1
  1206.             ELSE sync_hrac = 0
  1207.             END IF
  1208.  
  1209.             FOR i = PLAYER_NET_MIN TO PLAYER_NET_MAX 'send host data to each client
  1210.                 IF client(i).conn <> 0 THEN
  1211.                     sync.clientid = i
  1212.  
  1213.                     IF client(i).isdesynced = 1 THEN 'send sync var if client desynced
  1214.                         sync.synctime = TIMER(.001)
  1215.                         sync.itemstr = GETITEM_SYNC
  1216.                         PUT #client(i).conn, , sync
  1217.                         'PRINT "Resyncing client "; i 'TEMP DEBUG line
  1218.                     END IF
  1219.  
  1220.                     IF sync_stav = 1 AND client(i).isdesynced = 0 THEN 'send world state
  1221.                         sync.synctime = TIMER(.001)
  1222.                         sync.itemstr = GETITEM_STAV
  1223.                         PUT #client(i).conn, , sync
  1224.                         stav.synctime = TIMER(.001)
  1225.                         PUT #client(i).conn, , stav
  1226.                     END IF
  1227.  
  1228.                     IF sync_pbal = 1 AND client(i).isdesynced = 0 THEN 'send player balls
  1229.                         sync.synctime = TIMER(.001):
  1230.                         sync.itemstr = GETITEM_PBAL
  1231.                         PUT #client(i).conn, , sync
  1232.                         bal(1).synctime = TIMER(.001)
  1233.                         PUT #client(i).conn, , ipbal()
  1234.                     END IF
  1235.  
  1236.                     IF sync_bal = 1 AND client(i).isdesynced = 0 THEN 'send nonplayer balls
  1237.                         sync.synctime = TIMER(.001)
  1238.                         sync.itemstr = GETITEM_BAL
  1239.                         bal(stav.pocethracov + 1).synctime = TIMER(.001)
  1240.                         PUT #client(i).conn, , sync
  1241.                         PUT #client(i).conn, , ibal()
  1242.                     END IF
  1243.  
  1244.                     IF sync_hrac = 1 AND client(i).isdesynced = 0 THEN 'IF stav.gameplay = 0 OR stav.vitaz <> 0 THEN 'send players
  1245.                         FOR j = 1 TO PLAYER_NET_MAX: ihrac(j) = hrac(j): NEXT j 'fill transfer array
  1246.                         sync.itemstr = GETITEM_HRAC
  1247.                         sync.synctime = TIMER(.001)
  1248.                         PUT #client(i).conn, , sync
  1249.                         hrac(1).synctime = TIMER(.001)
  1250.                         PUT #client(i).conn, , ihrac()
  1251.                     END IF
  1252.  
  1253.                     IF game.sync_stats = 1 AND client(i).isdesynced = 0 THEN 'send match statistics
  1254.                         sync.itemstr = GETITEM_STAT
  1255.                         sync.synctime = TIMER(.001)
  1256.                         PUT #client(i).conn, , sync
  1257.                         PUT #client(i).conn, , stats()
  1258.                     END IF
  1259.  
  1260.                     IF game.newmsg = 1 AND client(i).isdesynced = 0 THEN 'send new messages
  1261.                         FOR j = 1 TO game.msgcount
  1262.                             IF msg(j).distribute = 1 AND msg(j).clientid <> i THEN
  1263.                                 sync.itemstr = GETITEM_MESG:
  1264.                                 sync.synctime = TIMER(.001):
  1265.                                 PUT #client(i).conn, , sync
  1266.                                 msg(j).synctime = TIMER(.001):
  1267.                                 PUT #client(i).conn, , msg(j)
  1268.                             END IF
  1269.                         NEXT j
  1270.                     END IF
  1271.  
  1272.                 END IF
  1273.             NEXT i
  1274.             game.sync_stats = 0 'clear send statistics flag
  1275.             game.newmsg = 0 'clear messages distributed flags
  1276.             FOR j = 1 TO game.msgcount
  1277.                 msg(j).distribute = 0
  1278.             NEXT j
  1279.         END IF
  1280.  
  1281.         '-------------------client side-------------------------------------------------------------------------------
  1282.         IF game.clienth <> 0 THEN 'client getting data from host
  1283.             DO
  1284.                 IF game.get_isdesync = 1 THEN 'flush acquired data if desynced
  1285.                     'PRINT "Resyncing... "; game.get_item 'TEMP debug line
  1286.                     DO
  1287.                         GET #game.clienth, , isync
  1288.                     LOOP UNTIL (NOT EOF(game.clienth) AND isync.syncstr = GETITEM_SYNC) OR (TIMER(.001) - game.get_time) > NET_TIMEOUT_SYNC
  1289.  
  1290.                     'GET #game.clienth, , flush$
  1291.  
  1292.                     IF NOT EOF(game.clienth) AND isync.syncstr = GETITEM_SYNC THEN
  1293.                         'PRINT "==================RESYNCED================ "; game.get_item; "-"; isync.itemstr 'TEMP debug line
  1294.                         game.get_item = isync.itemstr
  1295.                         game.get_isdesync = 0
  1296.                     ELSE
  1297.                         'PRINT "Desynced!!!!!!!!!!!!! "; game.get_item 'TEMP debug line
  1298.                     END IF
  1299.                 END IF
  1300.  
  1301.                 IF game.get_item = GETITEM_SYNC THEN 'first get sync info on what item to get next
  1302.                     GET #game.clienth, , isync
  1303.                     IF NOT EOF(game.clienth) THEN
  1304.                         IF isync.syncstr = GETITEM_SYNC THEN
  1305.                             game.clientid = isync.clientid
  1306.                             game.get_item = isync.itemstr
  1307.                             game.get_time = TIMER(.001)
  1308.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1309.                         ELSE
  1310.                             game.get_isdesync = 1
  1311.                         END IF
  1312.                     END IF
  1313.                 END IF
  1314.  
  1315.                 IF game.get_item = GETITEM_STAV THEN 'get world state
  1316.                     GET #game.clienth, , istav
  1317.                     IF NOT EOF(game.clienth) THEN
  1318.                         IF istav.syncstr = GETITEM_STAV THEN
  1319.                             IF istav.synctime > stav.synctime THEN
  1320.                                 stav = istav
  1321.                             END IF
  1322.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1323.                         ELSE
  1324.                             game.get_isdesync = 1
  1325.                         END IF
  1326.                         game.get_item = GETITEM_SYNC
  1327.                     END IF
  1328.                 END IF
  1329.  
  1330.                 IF game.get_item = GETITEM_PBAL THEN 'get player balls data
  1331.                     GET #game.clienth, , ipbal()
  1332.                     IF NOT EOF(game.clienth) THEN
  1333.                         IF (ipbal(1).syncstr = GETITEM_PBAL) THEN
  1334.                             IF (ipbal(1).synctime > bal(1).synctime) THEN 'AND (TIMER(.001) - game.timer_pballs) < (ipbal(1).synctime - bal(1).synctime + 0.05) THEN
  1335.                                 FOR i = 1 TO stav.pocethracov
  1336.                                     bal(i) = ipbal(i)
  1337.                                 NEXT i
  1338.                             END IF
  1339.                             game.timer_pballs = TIMER(.001)
  1340.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1341.                         ELSE
  1342.                             game.get_isdesync = 1
  1343.                         END IF
  1344.                         game.get_item = GETITEM_SYNC
  1345.                     END IF
  1346.                 END IF
  1347.  
  1348.                 IF game.get_item = GETITEM_BAL THEN 'get nonplayer balls data
  1349.                     GET #game.clienth, , ibal()
  1350.                     IF NOT EOF(game.clienth) THEN
  1351.                         IF (ibal(1).syncstr = GETITEM_BAL) THEN
  1352.                             IF (ibal(1).synctime > bal(stav.pocethracov + 1).synctime) THEN
  1353.                                 FOR i = 1 TO stav.pocetgulzvoleny
  1354.                                     bal(stav.pocethracov + i) = ibal(i)
  1355.                                 NEXT i
  1356.                             END IF
  1357.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1358.                         ELSE
  1359.                             game.get_isdesync = 1
  1360.                         END IF
  1361.                         game.get_item = GETITEM_SYNC
  1362.                     END IF
  1363.                 END IF
  1364.  
  1365.                 IF game.get_item = GETITEM_MESG THEN 'get message
  1366.                     GET #game.clienth, , imsg
  1367.                     IF NOT EOF(game.clienth) THEN
  1368.                         IF (imsg.syncstr = GETITEM_MESG) THEN
  1369.                             AddMessage imsg.text, imsg.col, imsg.time_dur, 0
  1370.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1371.                         ELSE
  1372.                             game.get_isdesync = 1
  1373.                         END IF
  1374.                         game.get_item = GETITEM_SYNC
  1375.                     END IF
  1376.                 END IF
  1377.  
  1378.                 IF game.get_item = GETITEM_HRAC THEN 'get players data
  1379.                     GET #game.clienth, , ihrac()
  1380.                     IF NOT EOF(game.clienth) THEN
  1381.                         IF ihrac(1).syncstr = GETITEM_HRAC THEN
  1382.                             IF ihrac(1).synctime > hrac(1).synctime THEN
  1383.                                 FOR i = 1 TO PLAYER_NET_MAX
  1384.                                     IF i <> game.clientid AND game.clientid > 0 THEN 'if another client's player
  1385.                                         hrac(i) = ihrac(i) 'get player data from host
  1386.                                         hrac(i).klavesy = i 'set player net controlled
  1387.                                     ELSE 'if my client player
  1388.                                         hrac(i).skore = ihrac(i).skore
  1389.                                         hrac(i).bal = ihrac(i).bal
  1390.                                         hrac(i).clientid = ihrac(i).clientid
  1391.                                     END IF
  1392.                                 NEXT i
  1393.                             END IF
  1394.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1395.                         ELSE
  1396.                             game.get_isdesync = 1
  1397.                         END IF
  1398.                         game.get_item = GETITEM_SYNC
  1399.                     END IF
  1400.                 END IF
  1401.  
  1402.                 IF game.get_item = GETITEM_STAT THEN 'get match statistics
  1403.                     GET #game.clienth, , istats()
  1404.                     IF NOT EOF(game.clienth) THEN
  1405.                         IF (istats(PLAYER_NET_MAX + 1, 1) + istats(PLAYER_NET_MAX + 1, 2) + istats(PLAYER_NET_MAX + 1, 3)) = (ASC(GETITEM_STAT, 1) + ASC(GETITEM_STAT, 2) + ASC(GETITEM_STAT, 3)) THEN
  1406.                             FOR i = 1 TO UBOUND(stats, 1)
  1407.                                 FOR j = 1 TO UBOUND(stats, 2)
  1408.                                     stats(i, j) = istats(i, j)
  1409.                                 NEXT j
  1410.                             NEXT i
  1411.                             game.sync_stats = 0
  1412.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1413.                         ELSE
  1414.                             game.get_isdesync = 1
  1415.                         END IF
  1416.                         game.get_item = GETITEM_SYNC
  1417.                     END IF
  1418.                 END IF
  1419.  
  1420.             LOOP UNTIL EOF(game.clienth)
  1421.  
  1422.             IF game.clientid > 0 THEN 'send clients data to host
  1423.                 sync.synctime = TIMER(.001)
  1424.  
  1425.                 'IF stav.gameplay = 1 THEN 'send client controller to host
  1426.                 sync.itemstr = GETITEM_KLAV
  1427.                 sync.synctime = TIMER(.001)
  1428.                 sync.isdesync = game.get_isdesync
  1429.                 PUT #game.clienth, , sync
  1430.                 PUT #game.clienth, , klav(hrac(game.clientid).klavesy)
  1431.                 'END IF
  1432.  
  1433.                 'IF stav.gameplay = 0 THEN 'send client player to host
  1434.                 sync.itemstr = GETITEM_HRAC
  1435.                 sync.synctime = TIMER(.001)
  1436.                 PUT #game.clienth, , sync
  1437.                 PUT #game.clienth, , hrac(game.clientid)
  1438.                 'END IF
  1439.  
  1440.                 IF game.newmsg = 1 AND game.msgcount > 0 THEN 'send client player to host
  1441.                     IF msg(game.msgcount).distribute = 1 THEN
  1442.                         sync.itemstr = GETITEM_MESG
  1443.                         sync.synctime = TIMER(.001)
  1444.                         PUT #game.clienth, , sync
  1445.                         PUT #game.clienth, , msg(game.msgcount)
  1446.                         msg(game.msgcount).distribute = 0
  1447.                     END IF
  1448.                     game.newmsg = 0
  1449.                 END IF
  1450.             END IF
  1451.  
  1452.             IF (TIMER(.001) - game.get_time) > NET_TIMEOUT_LIMIT THEN ResetNetGame
  1453.         END IF
  1454.     END IF
  1455.  
  1456. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1457. SUB CloseClientConn (clidx AS INTEGER)
  1458.     IF client(clidx).conn <> 0 THEN
  1459.         CLOSE client(clidx).conn
  1460.         client(clidx).conn = 0
  1461.     END IF
  1462.     hrac(clidx).clientid = 0
  1463.     hrac(clidx).klavesy = CPU_CONTROLID
  1464.     hrac(clidx).isready = 0
  1465.     bal(hrac(clidx).bal).complayer = 1
  1466.     IF stav.AIallowed = 0 AND klav(hrac(clidx).klavesy).typ = 4 THEN
  1467.         bal(hrac(clidx).bal).energia = 0
  1468.         CalculatePlayerState bal(hrac(clidx).bal)
  1469.     END IF
  1470.     AddMessage "Player " + RTRIM$(hrac(clidx).meno) + " was disconnected", MSG_COL, MSG_DUR_STD, 1
  1471.  
  1472. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1473. SUB ResetNetGame
  1474.     IF game.clienth <> 0 THEN 'try to close client connection
  1475.         CLOSE game.clienth
  1476.         game.clienth = 0
  1477.         game.clientid = 0
  1478.         stav.autostart_set = 0
  1479.         AddMessage "You have been disconnected from the host", MSG_COL, MSG_DUR_STD, 0
  1480.     END IF
  1481.     IF game.hosth <> 0 THEN 'try to close host service
  1482.         CLOSE game.hosth
  1483.         game.hosth = 0
  1484.         game.clientid = 0
  1485.         hrac(1).clientid = 0
  1486.         AddMessage "Game hosting ended", MSG_COL, MSG_DUR_STD, 0
  1487.     END IF
  1488.     game.isnetgame = 0
  1489.     FOR i = 1 TO PLAYER_COUNT_MAX
  1490.         hrac(i).clientid = 0
  1491.         IF (klav(hrac(i).klavesy).typ = 5) OR (i > PLAYER_NET_MAX AND klav(hrac(i).klavesy).typ <> 4) THEN
  1492.             hrac(i).klavesy = CPU_CONTROLID
  1493.             bal(hrac(i).bal).complayer = 1
  1494.         END IF
  1495.     NEXT i
  1496.  
  1497. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1498. SUB HostGame
  1499.     IF game.clienth <> 0 THEN ResetNetGame
  1500.     IF game.hosth = 0 THEN
  1501.         game.hosth = _OPENHOST("TCP/IP:" + STR$(game.host_port))
  1502.         IF game.hosth <> 0 THEN
  1503.             game.isnetgame = 1
  1504.             game.clientid = 1
  1505.             hrac(1).clientid = 1
  1506.             AddMessage "Game hosting started", MSG_COL, MSG_DUR_STD, 0
  1507.         ELSE
  1508.             game.isnetgame = 0
  1509.         END IF
  1510.     ELSE
  1511.         ResetNetGame
  1512.     END IF
  1513.  
  1514. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1515. SUB JoinGame
  1516.     IF game.hosth <> 0 THEN ResetNetGame
  1517.     IF game.clienth = 0 THEN
  1518.         AddMessage "Connecting to" + RTRIM$(STR$(game.host_port)) + ":" + RTRIM$(game.host_ip) + "...", MSG_COL, MSG_DUR_STD, 0
  1519.         DisplayMessages
  1520.         _DISPLAY
  1521.         game.clienth = _OPENCLIENT("TCP/IP:" + STR$(game.host_port) + ":" + RTRIM$(game.host_ip))
  1522.         IF game.clienth <> 0 THEN
  1523.             game.isnetgame = 1
  1524.             game.get_item = GETITEM_SYNC
  1525.             game.get_isdesync = 0
  1526.             game.get_time = TIMER(.001)
  1527.             game.timer_pballs = TIMER(.001)
  1528.             AddMessage "Connection opened, joining the game...", MSG_COL, MSG_DUR_STD, 0
  1529.         ELSE
  1530.             game.isnetgame = 0
  1531.         END IF
  1532.     ELSE
  1533.         CLOSE game.clienth
  1534.         ResetNetGame
  1535.     END IF
  1536.  
  1537. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1538. FUNCTION InputText$ (meno AS STRING, xpos AS INTEGER, ypos AS INTEGER, col AS INTEGER, inputwidth AS _BYTE, hint AS STRING, initval AS _BYTE, digitonly AS _BYTE) 'get text input from keyboard input
  1539.     clrstr$ = ""
  1540.     tmpstr$ = ""
  1541.     IF initval = 1 THEN res$ = RTRIM$(meno) ELSE res$ = ""
  1542.     FOR i = 1 TO inputwidth: clrstr$ = clrstr$ + " ": NEXT i
  1543.     COLOR col
  1544.     DO
  1545.         DO
  1546.             _LIMIT FPS
  1547.             k = _KEYHIT
  1548.         LOOP UNTIL k <> 0
  1549.         IF ((digitonly = 0 AND k >= 33 AND k <= 126) OR (digitonly = 1 AND k >= 48 AND k <= 57)) AND LEN(res$) < LEN(meno) THEN res$ = res$ + CHR$(k)
  1550.         IF k = 8 AND LEN(res$) > 0 THEN res$ = LEFT$(res$, LEN(res$) - 1)
  1551.         IF res$ = "" THEN tmpstr$ = hint ELSE tmpstr$ = res$
  1552.         _PRINTSTRING (xpos, ypos), clrstr$
  1553.         _PRINTSTRING (xpos, ypos), tmpstr$
  1554.         _DISPLAY
  1555.     LOOP UNTIL k = 13 OR k = 27
  1556.     IF k = 13 THEN InputText$ = res$
  1557.     IF k = 27 THEN InputText$ = CHR$(27)
  1558.  
  1559. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1560. SUB PrintMenuItem (id AS INTEGER, amenu AS tmenu, menucode AS INTEGER, playercode AS INTEGER, rollable AS INTEGER, s AS STRING, fixedwidth AS INTEGER, deltax AS INTEGER, lineoff AS INTEGER, col AS INTEGER) 'print horizontaly centered text at line y
  1561.     slen = LEN(RTRIM$(s)) * FONT_W
  1562.     IF fixedwidth > 0 THEN slen = fixedwidth
  1563.     px = INT((game.maxx - slen) / 2) + deltax
  1564.     py = amenu.ypos + (id - 1) * amenu.item_height
  1565.     COLOR col
  1566.     _PRINTSTRING (px, py), s
  1567.     IF id = amenu.item_active THEN
  1568.         IF rollable = 1 THEN
  1569.             COLOR amenu.cursor_col
  1570.             _PRINTSTRING (amenu.cursor_xpos - 10 - FONT_W, py), "<"
  1571.             _PRINTSTRING (amenu.cursor_xpos + amenu.cursor_width + 10, py), ">"
  1572.         END IF
  1573.         amenu.menucode = menucode
  1574.         amenu.playercode = playercode
  1575.     END IF
  1576. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1577. SUB PrintMenuCursor (amenu AS tmenu) 'print menu cursor
  1578.     py = amenu.ypos + (amenu.item_active - 1) * amenu.item_height
  1579.     COLOR amenu.cursor_col
  1580.     LINE (amenu.cursor_xpos - amenu.cursor_out, game.maxy - (py - amenu.cursor_out))-(amenu.cursor_xpos + amenu.cursor_out + amenu.cursor_width, game.maxy - (py + FONT_H) - amenu.cursor_out), amenu.cursor_col, B
  1581.  
  1582. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1583. SUB PauseGame 'pause gameplay
  1584.     winstr$ = "-> Game paused <-"
  1585.     COLOR MSG_COL
  1586.     _PRINTSTRING ((game.maxx - LEN(winstr$) * 10) / 2, (game.maxy - 50) / 2), winstr$
  1587.  
  1588. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1589. SUB DrawBall (baln AS gula) 'draw ball
  1590.     CONST BSEGM = 10
  1591.     koef = (10 + 90 * game.printdebug) * FPS / 100
  1592.     bcol = baln.col
  1593.     IF baln.freezedtime > 0 THEN bcol = 27
  1594.     IF baln.riadena = 1 THEN
  1595.         CIRCLE (baln.x, baln.y), baln.r - 1, baln.col
  1596.         CIRCLE (baln.x, baln.y), baln.r + 1, baln.col
  1597.         FOR i = 1 TO BSEGM
  1598.             CIRCLE (baln.x, baln.y), i * (baln.r / 2 - 2) / BSEGM, bcol, 0, baln.energia / stav.energy_player_start * _PI * 2
  1599.         NEXT i
  1600.         LINE (baln.x, baln.y)-(baln.x + baln.v.x * koef, baln.y + baln.v.y * koef), baln.col 'draw actual velocity
  1601.         IF game.printdebug = 1 THEN LINE (baln.x, baln.y)-(baln.x + baln.vi.x * koef, baln.y + baln.vi.y * koef), bal(baln.protivnik).col 'draw intended velocity
  1602.     END IF
  1603.     IF baln.i = stav.baljeto THEN
  1604.         CIRCLE (baln.x, baln.y), baln.r * 2 / 3, 15
  1605.     END IF
  1606.     IF baln.i > stav.pocethracov THEN
  1607.         reflectfrom = ATN((1.25 * stav.maxy - baln.y) / (1.25 * stav.maxx - baln.x))
  1608.         CIRCLE (baln.x, baln.y), baln.r - 5, 28, reflectfrom, reflectfrom + _PI / 8
  1609.         CIRCLE (baln.x, baln.y), baln.r - 5, 28, reflectfrom + _PI / 8 + _PI / 32, reflectfrom + _PI / 4
  1610.         CIRCLE (baln.x, baln.y), baln.r - 10, 28, reflectfrom, reflectfrom + _PI / 8
  1611.         CIRCLE (baln.x, baln.y), baln.r - 10, 28, reflectfrom + _PI / 8 + _PI / 32, reflectfrom + _PI / 4
  1612.     END IF
  1613.  
  1614.     CIRCLE (baln.x, baln.y), baln.r, baln.col
  1615.  
  1616. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1617. SUB DrawScore 'Draw player score
  1618.     WINDOW (0, 0)-(game.maxx, game.maxy)
  1619.     FOR i = 1 TO stav.pocethracov
  1620.         scxl = SCORE_DELTA_X * -(game.maxx >= 4 * SCORE_DELTA_X) + game.maxx / 4 * -(game.maxx < 4 * SCORE_DELTA_X)
  1621.         dx = (game.maxx - 4 * scxl) / 2
  1622.         scx = dx + scxl * ((i - 4 * INT((i - 1) / 4)) - 1)
  1623.         scty = SCORE_DELTA_Y + 15 * INT((i - 1) / 4)
  1624.         scly = game.maxy - SCORE_DELTA_Y - 15 * INT((i - 1) / 4)
  1625.         COLOR bal(hrac(i).bal).col
  1626.         _PRINTSTRING (scx + (LEN(hrac(i).meno) - LEN(RTRIM$(hrac(i).meno))) * 8, scty - 10), RTRIM$(hrac(i).meno) + " -" + STR$(hrac(i).skore)
  1627.         LINE (scx + SCORE_NAME_LEN, scly)-(scx + SCORE_NAME_LEN + bal(hrac(i).bal).energia, scly + 7), bal(hrac(i).bal).col, BF
  1628.         IF bal(hrac(i).bal).energia <= 0 THEN
  1629.             _PRINTSTRING (scx + SCORE_NAME_LEN, scty - 10), "OUT"
  1630.         END IF
  1631.     NEXT i
  1632.     WINDOW (0, 0)-(stav.maxx, stav.maxy)
  1633.  
  1634. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1635. SUB CalculatePlayerState (baln AS gula) 'calculate players states
  1636.     DIM od AS SINGLE
  1637.     '-----------------------energy decreasing-------------------------------------
  1638.     IF baln.freezedtime > 0 THEN
  1639.         baln.freezedtime = baln.freezedtime - 1
  1640.     ELSE
  1641.         baln.energia = baln.energia - (stav.energydec * -(stav.baljeto = baln.i)) * game.fpscoef
  1642.     END IF
  1643.     IF baln.energia <= 0 THEN
  1644.         baln.riadena = 0
  1645.         pocetriadenych = 0
  1646.         FOR i = 1 TO stav.pocethracov
  1647.             IF bal(hrac(i).bal).riadena = 1 THEN
  1648.                 pocetriadenych = pocetriadenych + 1
  1649.                 priebeznyvitaz = i
  1650.             END IF
  1651.         NEXT i
  1652.         IF pocetriadenych > 1 THEN
  1653.             stav.baljeto = stav.balbudeto
  1654.             stav.balbudeto = nearestPlayer(bal(stav.baljeto), 0, 0)
  1655.             FOR i = 1 TO stav.pocetgulx
  1656.                 IF bal(i).i <> stav.baljeto AND bal(i).riadena = 1 THEN bal(i).protivnik = stav.baljeto
  1657.             NEXT i
  1658.         END IF
  1659.         IF pocetriadenych = 1 THEN
  1660.             stav.vitaz = priebeznyvitaz
  1661.             hrac(stav.vitaz).skore = hrac(stav.vitaz).skore + 1
  1662.             IF hrac(stav.vitaz).skore >= stav.winscore THEN game.autostart_time = TIMER + DELAY_MATCHEND ELSE game.autostart_time = TIMER + DELAY_ROUNDSTART
  1663.             DrawScore
  1664.         END IF
  1665.     END IF
  1666.     '-----------------------dodging time-----------------------------------------
  1667.     IF baln.klucka_time = 0 AND baln.klucka_trvanie = 0 AND baln.klucka_pocet = 0 THEN
  1668.         baln.klucka_time = INT(RND * strat(baln.strat).klucka_avgtm + strat(baln.strat).klucka_avgtm / 2) / game.fpscoef
  1669.         baln.klucka_pocet = strat(baln.strat).klucka_pocet
  1670.     END IF
  1671.     IF baln.klucka_time > 0 THEN
  1672.         baln.klucka_time = baln.klucka_time - 1
  1673.     END IF
  1674.     IF baln.klucka_time = 0 AND baln.klucka_trvanie > 0 THEN
  1675.         baln.klucka_trvanie = baln.klucka_trvanie - 1
  1676.     END IF
  1677.     IF baln.klucka_time = 0 AND baln.klucka_trvanie = 0 AND baln.klucka_pocet > 0 THEN
  1678.         baln.klucka_pocet = baln.klucka_pocet - 1
  1679.         baln.klucka_trvanie = INT(RND * strat(baln.strat).klucka_trvanie + strat(baln.strat).klucka_trvanie / 2) / game.fpscoef
  1680.         baln.klucka_uhol = RND * strat(baln.strat).klucka_uhol * (2 * strat(baln.strat).klucka_uholodch) + strat(baln.strat).klucka_uhol * (1 - strat(baln.strat).klucka_uholodch) * (3 - INT(RND * 2 + 1) * 2)
  1681.     END IF
  1682.     '-----------------------choose nearest enemy if tagged-----------------------
  1683.     IF stav.baljeto = baln.i THEN
  1684.         novyprotivnik = nearestPlayer(baln, 0, -(baln.energia > (stav.energy_player_start * strat(baln.strat).adrenalin_energy)))
  1685.         IF novyprotivnik <> 0 THEN baln.protivnik = novyprotivnik
  1686.         stav.balbudeto = nearestPlayer(baln, 0, 0)
  1687.     END IF
  1688.     '-----------------------boosting time----------------------------------------
  1689.     IF baln.boost_time > 0 AND baln.boost_trvanie = 0 THEN baln.boost_time = baln.boost_time - 1
  1690.     IF baln.boost_trvanie > 0 THEN baln.boost_trvanie = baln.boost_trvanie - 1
  1691.  
  1692.     '-----------------------reaction drop time-----------------------------------
  1693.     IF baln.reactdrop_time > 0 THEN baln.reactdrop_time = baln.reactdrop_time - 1
  1694.     IF baln.reactdrop_time <= 0 AND baln.reactdrop_dur > 0 THEN baln.reactdrop_dur = baln.reactdrop_dur - 1
  1695.     IF baln.reactdrop_time <= 0 AND baln.reactdrop_dur <= 0 THEN
  1696.         IF baln.energia < (stav.energy_player_start * strat(baln.strat).adrenalin_energy) THEN reactkoef = (1 - strat(baln.strat).adrenalin_rate) ELSE reactkoef = 1
  1697.         baln.reactdrop_time = INT((RND * strat(baln.strat).reactdrop_per * reactkoef + strat(baln.strat).reactdrop_per * reactkoef / 2) / game.fpscoef)
  1698.         baln.reactdrop_dur = INT((RND * strat(baln.strat).reactdrop_dur * reactkoef + strat(baln.strat).reactdrop_dur * reactkoef / 2) / game.fpscoef)
  1699.         baln.smer_odchyl = (RND * strat(baln.strat).chybasmeru - strat(baln.strat).chybasmeru / 2) * pi 'apply random radian deviance from intended direction
  1700.     END IF
  1701.  
  1702. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1703. FUNCTION nearestPlayer (baln AS gula, mateonly AS _BYTE, weighted AS _BYTE) 'returns nearest opponent idx
  1704.     CONST NEAREST_WEIGHTCOEF = 10.0 'coeficient for energy inclusion into weight
  1705.     DIM weight, vzd, vzdakt, vzdakt_winner AS SINGLE
  1706.     DIM nearest_winner AS INTEGER
  1707.     vzdakt = 0
  1708.     nearestPlayer = 0
  1709.     vzdakt_winner = 0
  1710.     nearest_winner = 0
  1711.     FOR i = 1 TO stav.pocetgulx
  1712.         IF bal(i).i <> baln.i AND bal(i).riadena = 1 AND (mateonly = 1 IMP i <> stav.baljeto) THEN
  1713.             weight = weighted * (stav.energy_player_start - bal(i).energia) * NEAREST_WEIGHTCOEF
  1714.             vzd = SQR((baln.x - bal(i).x) ^ 2 + (baln.y - bal(i).y) ^ 2) + weight
  1715.             IF nearestPlayer = 0 OR vzd < vzdakt THEN
  1716.                 nearestPlayer = i
  1717.                 vzdakt = vzd
  1718.             END IF
  1719.             IF hrac(bal(i).hrac).skore = stav.winscore - 1 AND (vzd < vzdakt_winner OR nearest_winner = 0) THEN
  1720.                 nearest_winner = i
  1721.                 vzdakt_winner = vzd
  1722.             END IF
  1723.         END IF
  1724.     NEXT i
  1725.     IF weighted = 1 AND nearest_winner > 0 THEN nearestPlayer = nearest_winner
  1726.  
  1727. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1728. SUB GenerateAIControl (baln AS gula) 'Decide computer player's control keys hits
  1729.     DIM dd AS SINGLE, vid AS SINGLE, vd AS SINGLE, eid AS SINGLE, vnd AS SINGLE, ved AS SINGLE, did AS SINGLE, thit AS SINGLE
  1730.     DIM rot_klucka AS SINGLE, rot_border AS SINGLE, rot_enemy AS SINGLE, rot_mate AS SINGLE
  1731.     DIM siner AS SINGLE, coser AS SINGLE, sineri AS SINGLE, coseri AS SINGLE
  1732.     DIM sinevr AS SINGLE, cosevr AS SINGLE, sinmt AS SINGLE, cosnmt AS SINGLE
  1733.     DIM okrajrot AS SINGLE, enemy_dist_factor AS SINGLE, smerdookraju AS INTEGER, nearmate AS INTEGER
  1734.     DIM d AS vektor, vi AS vektor, ei AS vektor, ve AS vektor, di AS vektor, dm AS vektor
  1735.  
  1736.     vi = baln.vi 'player's intended speed vector copy
  1737.     vid = SQR(vi.x ^ 2 + vi.y ^ 2)
  1738.     vi.x = vi.x / vid * stav.vmax
  1739.     vi.y = vi.y / vid * stav.vmax
  1740.     vd = SQR(baln.v.x ^ 2 + baln.v.y ^ 2)
  1741.     ve = bal(baln.protivnik).v 'enemy speed vector copy
  1742.     ved = SQR(ve.x ^ 2 + ve.y ^ 2)
  1743.  
  1744.     d.x = bal(baln.protivnik).x - baln.x 'player to enemy postition vector
  1745.     d.y = bal(baln.protivnik).y - baln.y
  1746.     dd = SQR(d.x ^ 2 + d.y ^ 2)
  1747.     coser = (d.x * baln.v.x + d.y * baln.v.y) / (dd * vd)
  1748.     siner = (d.x * baln.v.y - d.y * baln.v.x) / (dd * vd)
  1749.     cosevr = (ve.x * baln.v.x + ve.y * baln.v.y) / (ved * vd)
  1750.     sinevr = (ve.x * baln.v.y - ve.y * baln.v.x) / (ved * vd)
  1751.  
  1752.     '!EXPERIMENTAL CODE! - try more exact enemy future position vector, but  does not work so well as used approximation
  1753.     'di.x = d.x + ve.x * d.x / ((((d.x / dd) + (ve.x / ved)) * stav.vmax) - ve.x) 'new intended direction to enemy
  1754.     'di.y = d.y + ve.y * d.y / ((((d.y / dd) + (ve.y / ved)) * stav.vmax) - ve.y) 'new intended direction to enemy
  1755.     '!EXPERIMENTAL CODE! end
  1756.  
  1757.     di.x = d.x + ve.x * dd / vd * ABS(siner) 'player to enemy approximate future position vector, future enemy position more relevant when enemy closer
  1758.     di.y = d.y + ve.y * dd / vd * ABS(siner)
  1759.     did = SQR(di.x ^ 2 + di.y ^ 2)
  1760.     coseri = (di.x * baln.v.x + di.y * baln.v.y) / (did * vd)
  1761.     sineri = (di.x * baln.v.y - di.y * baln.v.x) / (did * vd)
  1762.  
  1763.     rot_enemy = 0 'rotation away from enemy in rad
  1764.     rot_border = 0 'rotation away from border in rad
  1765.     rot_mate = 0 'rotation towards nearest mate in rad
  1766.     rot_odchyl = 0 'baln.smer_odchyl * -(baln.reactdrop_dur = 1) 'apply direction error if react time
  1767.     smerdookraju = 0 'is players direction to border
  1768.  
  1769.     IF NOT (stav.baljeto = baln.i) THEN 'decide flee action
  1770.         enemy_dist_factor = 1 * -(dd <= strat(baln.strat).detekcia_vzdfull) + (1 - (dd - strat(baln.strat).detekcia_vzdfull) / _
  1771.           (strat(baln.strat).detekcia_vzdmin - strat(baln.strat).detekcia_vzdfull)) * -(dd > strat(baln.strat).detekcia_vzdfull AND dd <= strat(baln.strat).detekcia_vzdmin)' factor=1 if enemy distance within treshold, then decreasing
  1772.  
  1773.         IF enemy_dist_factor > 0 THEN 'if enemy in detection distance, rotate away
  1774.             rot_enemy = -(coseri > 0) * -(ABS(sineri) <= strat(baln.strat).detekcia_sin) * (-(sineri > 0) + (sineri < 0)) * strat(baln.strat).detekcia_rot * enemy_dist_factor * game.fpscoef
  1775.  
  1776.             '!EXPERIMENTAL CODE! - try better rotation from enemy
  1777.             'strat(baln.strat).detekcia_rot = 10 * pi / FPS 'TEMPORARY MODIFICATION
  1778.             'cosevi = (di.x * vi.x + di.y * vi.y) / (did * vid)
  1779.             'sinevi = (di.x * vi.y - di.y * vi.x) / (did * vid)
  1780.             'cosvvi = (v.x * vi.x + v.y * vi.y) / (vd * vid)
  1781.             'sinvvi = (v.x * vi.y - v.y * vi.x) / (vd * vid)
  1782.             'IF (coseri > 0) AND (ABS(sineri) <= strat(baln.strat).detekcia_sin) THEN
  1783.             '    rot_dir = -(sineri > 0) + (sineri < 0)
  1784.             '    IF NOT ((cosevi > 0 AND (ABS(sinevi) <= strat(baln.strat).detekcia_sin)) AND NOT (rot_dir = (-(sinevi > 0) + (sinevi < 0)))) THEN
  1785.             '        rot_enemy = rot_dir * strat(baln.strat).detekcia_rot * enemy_dist_factor * game.fpscoef
  1786.             '    END IF
  1787.             'END IF
  1788.             '!EXPERIMENTAL CODE! end
  1789.  
  1790.         END IF
  1791.  
  1792.         okrajrot = strat(baln.strat).okraj_rot 'border unit rotation in rad
  1793.         IF baln.x < strat(baln.strat).okraj AND vi.x < 0 THEN
  1794.             rot_border = (okrajrot * (vi.y >= 0) + okrajrot * -(vi.y < 0)) '* (1 - enemy_dist_factor)
  1795.             smerdookraju = 1
  1796.         END IF
  1797.         IF baln.x > stav.maxx - strat(baln.strat).okraj AND vi.x > 0 THEN
  1798.             rot_border = (okrajrot * -(vi.y >= 0) + okrajrot * (vi.y < 0)) '* (1 - enemy_dist_factor)
  1799.             smerdookraju = 1
  1800.         END IF
  1801.         IF baln.y < strat(baln.strat).okraj AND vi.y < 0 THEN
  1802.             rot_border = (okrajrot * (vi.x >= 0) + okrajrot * (vi.x < 0)) '* (1 - enemy_dist_factor)
  1803.             smerdookraju = 1
  1804.         END IF
  1805.         IF baln.y > stav.maxy - strat(baln.strat).okraj AND vi.y > 0 THEN
  1806.             rot_border = (okrajrot * (vi.x >= 0) + okrajrot * -(vi.x < 0)) '* (1 - enemy_dist_factor)
  1807.             smerdookraju = 1
  1808.         END IF
  1809.     END IF
  1810.  
  1811.     rot_klucka = baln.klucka_uhol * -(baln.klucka_time = 0) * -(baln.klucka_trvanie = 1) * -(stav.baljeto <> baln.i) * _
  1812.       (dd > strat(baln.strat).mate_distmin OR (coseri < 0 AND sineri >= 0 AND baln.klucka_uhol > 0 AND baln.klucka_uhol < pi) OR (coseri < 0 AND sineri <= 0 AND baln.klucka_uhol < 0 AND baln.klucka_uhol > -pi)) 'apply dodge if dodging time - TEMPORARY DISABLED!!!
  1813.  
  1814.  
  1815.     IF baln.i = bal(stav.baljeto).protivnik THEN 'if ball being chased
  1816.         nearmate = nearestPlayer(baln, 1, -(baln.energia > (stav.energy_player_start * strat(baln.strat).adrenalin_energy)))
  1817.         IF nearmate > 0 THEN
  1818.             dm.x = bal(nearmate).x - baln.x
  1819.             dm.y = bal(nearmate).y - baln.y
  1820.             dmd = SQR(dm.x ^ 2 + dm.y ^ 2)
  1821.             cosnm = (dm.x * baln.v.x + dm.y * baln.v.y) / (dmd * vd)
  1822.             sinnm = (dm.x * baln.v.y - dm.y * baln.v.x) / (dmd * vd)
  1823.             IF (rot_border = 0) AND (dmd > strat(baln.strat).mate_distmin) AND (((coseri < strat(baln.strat).mate_cosenmax) AND (dd < strat(baln.strat).mate_en_distmin)) OR ((coseri < 0) AND (dd > strat(baln.strat).mate_en_distmin))) THEN
  1824.                 rot_mate = ((sinnm > 0) * strat(baln.strat).mate_rot * game.fpscoef - (sinnm <= 0) * strat(baln.strat).mate_rot * game.fpscoef) 'rotate towards mate
  1825.             END IF
  1826.         END IF
  1827.     END IF
  1828.  
  1829.     RotateVector vi, rot_border + rot_klucka + rot_odchyl + rot_enemy + rot_mate 'aplly all calculated rotations to intended velocity
  1830.  
  1831.     IF stav.baljeto = baln.i THEN 'chasing direction intended
  1832.         IF baln.reactdrop_time > 0 THEN 'if time is up for reaction
  1833.             baln.boostintended = -(-(coser > 0) AND -(cosevr > 0) AND -(ABS(sinevr) < strat(baln.strat).boost_chasesin) AND baln.boost_time = 0 AND smerdookraju = 0)
  1834.             baln.vi.x = stav.vmax * di.x / did
  1835.             baln.vi.y = stav.vmax * di.y / did
  1836.         END IF
  1837.     ELSE 'flee direction intended
  1838.         baln.boostintended = -(-(coser < 0) AND baln.boost_time = 0 AND dd < (RND * strat(baln.strat).boost_dist + 2 * PLAY_BALL_R) AND smerdookraju = 0)
  1839.         baln.vi.x = vi.x
  1840.         baln.vi.y = vi.y
  1841.     END IF
  1842.  
  1843. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1844. SUB ReadGamepad (klavn AS tklavesy) 'read state of gamepad
  1845.     di = _DEVICEINPUT(klavn.devid)
  1846.     IF klavn.typ = 3 THEN
  1847.         klavn.gvektor.x = _AXIS(klavn.xaxis)
  1848.         klavn.gvektor.y = -_AXIS(klavn.yaxis)
  1849.         klavn.isshoota = _BUTTON(klavn.shoota)
  1850.         klavn.isshootb = _BUTTON(klavn.shootb)
  1851.         klavn.isup = (_AXIS(klavn.dyaxis) = -1)
  1852.         klavn.isdown = (_AXIS(klavn.dyaxis) = 1)
  1853.         klavn.isleft = (_AXIS(klavn.dxaxis) = -1)
  1854.         klavn.isright = (_AXIS(klavn.dxaxis) = 1)
  1855.         IF klavn.gvektor.x > 0.5 THEN klavn.isright = -1
  1856.         IF klavn.gvektor.x < -0.5 THEN klavn.isleft = -1
  1857.         IF klavn.gvektor.y > 0.5 THEN klavn.isup = -1
  1858.         IF klavn.gvektor.y < -0.5 THEN klavn.isdown = -1
  1859.  
  1860.         IF klavn.buttonstate <> (klavn.isshoota + klavn.isshootb * 2 + klavn.isup * 4 + klavn.isdown * 8 + klavn.isright * 16 + klavn.isleft * 32) THEN
  1861.             klavn.buttonstatechanged = -1
  1862.             klavn.buttonstatetimer = 50
  1863.         ELSE
  1864.             klavn.buttonstatechanged = 0
  1865.         END IF
  1866.         klavn.buttonstate = (klavn.isshoota + klavn.isshootb * 2 + klavn.isup * 4 + klavn.isdown * 8 + klavn.isright * 16 + klavn.isleft * 32)
  1867.     END IF
  1868.     IF klavn.buttonstatetimer > 0 THEN klavn.buttonstatetimer = klavn.buttonstatetimer - 1
  1869.     IF klavn.buttonstatetimer = 0 THEN klavn.buttonstatechanged = -1
  1870.  
  1871. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1872. SUB ResolvePlayerControl (baln AS gula, klavn AS tklavesy) 'perform players control keys actions
  1873.     DIM vn AS vektor, gvector AS vektor
  1874.  
  1875.     IF baln.complayer = 0 THEN 'read human player controls
  1876.         IF klavn.typ = 1 THEN 'read keyboard control
  1877.             klavn.isright = _KEYDOWN(klavn.right)
  1878.             klavn.isleft = _KEYDOWN(klavn.left)
  1879.             klavn.isdown = _KEYDOWN(klavn.down)
  1880.             klavn.isup = _KEYDOWN(klavn.up)
  1881.             klavn.isshoota = _KEYDOWN(klavn.shoota)
  1882.             klavn.isshootb = _KEYDOWN(klavn.shootb)
  1883.             baln.vi.x = baln.v.x
  1884.             baln.vi.y = baln.v.y
  1885.         END IF
  1886.  
  1887.         IF klavn.typ = 3 THEN 'read gamepad control
  1888.             baln.vi.x = 0
  1889.             baln.vi.y = 0
  1890.             ReadGamepad klavn
  1891.             baln.vi.x = klavn.gvektor.x * stav.vmax
  1892.             baln.vi.y = klavn.gvektor.y * stav.vmax
  1893.             IF ABS(klavn.gvektor.x) > GAMESTICK_DEADZONE OR ABS(klavn.gvektor.y) > GAMESTICK_DEADZONE THEN
  1894.                 klavn.isright = (baln.v.x < baln.vi.x) 'set control keys status for desired direction
  1895.                 klavn.isleft = (baln.v.x > baln.vi.x)
  1896.                 klavn.isup = (baln.v.y < baln.vi.y)
  1897.                 klavn.isdown = (baln.v.y > baln.vi.y)
  1898.             END IF
  1899.             IF klavn.isshootb AND game.isnetgame = 0 THEN stav.koniec_kola = 2
  1900.         END IF
  1901.  
  1902.         IF klavn.typ = 5 AND game.isnetgame = 1 THEN
  1903.             IF game.clienth <> 0 THEN ' reset network controller state on client
  1904.                 klavn.isright = (baln.v.x < baln.vi.x) 'set control keys status for desired direction
  1905.                 klavn.isleft = (baln.v.x > baln.vi.x)
  1906.                 klavn.isup = (baln.v.y < baln.vi.y)
  1907.                 klavn.isdown = (baln.v.y > baln.vi.y)
  1908.                 'klavn.isright = 0
  1909.                 'klavn.isleft = 0
  1910.                 'klavn.isup = 0
  1911.                 'klavn.isdown = 0
  1912.                 klavn.isshoota = 0
  1913.             END IF
  1914.             baln.vi.x = baln.v.x
  1915.             baln.vi.y = baln.v.y
  1916.         END IF
  1917.  
  1918.     ELSE 'get controls of computer player
  1919.         IF baln.riadena = 1 THEN GenerateAIControl baln
  1920.         klavn.isright = (baln.v.x < baln.vi.x) 'set control keys status for desired direction
  1921.         klavn.isleft = (baln.v.x > baln.vi.x)
  1922.         klavn.isup = (baln.v.y < baln.vi.y)
  1923.         klavn.isdown = (baln.v.y > baln.vi.y)
  1924.         klavn.isshoota = -baln.boostintended
  1925.     END IF
  1926.  
  1927.     IF baln.freezedtime <= 0 AND baln.riadena = 1 THEN 'apply acquired controls to player movement
  1928.         vn.x = baln.v.x
  1929.         vn.y = baln.v.y
  1930.  
  1931.         game.fpscoef = stav.fps / game.fpsactual
  1932.         IF klavn.isright THEN vn.x = vn.x + baln.a.x * game.fpscoef 'accelerate player according to control keys states
  1933.         IF klavn.isleft THEN vn.x = vn.x - baln.a.x * game.fpscoef
  1934.         IF klavn.isdown THEN vn.y = vn.y - baln.a.y * game.fpscoef
  1935.         IF klavn.isup THEN vn.y = vn.y + baln.a.x * game.fpscoef
  1936.         IF klavn.isshoota AND baln.boost_time = 0 AND stav.boostallowed = 1 THEN 'set boost state
  1937.             baln.boost_trvanie = BOOST_TRVANIE / game.fpscoef
  1938.             baln.boost_time = BOOST_TIME / game.fpscoef
  1939.         END IF
  1940.         vnd = SQR(vn.x ^ 2 + vn.y ^ 2) 'limit actual velocity to max. velocity
  1941.         IF vnd > stav.vmax THEN
  1942.             vn.x = vn.x * stav.vmax / vnd
  1943.             vn.y = vn.y * stav.vmax / vnd
  1944.         END IF
  1945.  
  1946.         IF baln.boost_trvanie > 0 THEN 'add some extra speed if boosted
  1947.             vn.x = vn.x + stav.vmax * BOOST_V_KOEF * (baln.boost_trvanie / BOOST_TRVANIE) * game.fpscoef * vn.x / vnd
  1948.             vn.y = vn.y + stav.vmax * BOOST_V_KOEF * (baln.boost_trvanie / BOOST_TRVANIE) * game.fpscoef * vn.y / vnd
  1949.         END IF
  1950.  
  1951.         baln.v.x = vn.x 'apply controler modified velocity
  1952.         baln.v.y = vn.y
  1953.     END IF
  1954.  
  1955. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1956. SUB PerformMovement (baln AS gula) 'Calculate ball movement
  1957.     baln.x = baln.x + baln.v.x * game.fpscoef
  1958.     baln.y = baln.y + baln.v.y * game.fpscoef
  1959.     baln.v.x = baln.v.x * stav.odpor ^ game.fpscoef
  1960.     baln.v.y = baln.v.y * stav.odpor ^ game.fpscoef
  1961.     IF (baln.x + baln.r > stav.maxx) THEN
  1962.         baln.v.x = -baln.v.x
  1963.         baln.x = stav.maxx - baln.r
  1964.     END IF
  1965.     IF (baln.x - baln.r < stav.minx) THEN
  1966.         baln.v.x = -baln.v.x
  1967.         baln.x = stav.minx + baln.r
  1968.     END IF
  1969.     IF (baln.y + baln.r > stav.maxy) THEN
  1970.         baln.v.y = -baln.v.y
  1971.         baln.y = stav.maxy - baln.r
  1972.     END IF
  1973.     IF (baln.y - baln.r < stav.miny) THEN
  1974.         baln.v.y = -baln.v.y
  1975.         baln.y = stav.miny + baln.r
  1976.     END IF
  1977.  
  1978. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1979. SUB BounceTest (bala AS gula, balb AS gula) 'Balls bounce test
  1980.     IF (bala.x - balb.x) * (bala.x - balb.x) + (bala.y - balb.y) * (bala.y - balb.y) <= (bala.r + balb.r + 0) * (bala.r + balb.r + 0) THEN
  1981.         PerformBounce bala, balb
  1982.         IF bala.riadena AND balb.riadena AND bala.freezedtime <= 0 AND balb.freezedtime <= 0 AND (stav.baljeto = bala.i OR stav.baljeto = balb.i) THEN 'balb.col = bal(bala.protivnik).col
  1983.             newjeto = bala.i * -(stav.baljeto <> bala.i) + balb.i * -(stav.baljeto <> balb.i)
  1984.             IF stav.baljeto <= PLAYER_NET_MAX AND newjeto <= PLAYER_NET_MAX AND (game.isnetgame = 0 OR game.hosth <> 0) THEN
  1985.                 stats(stav.baljeto, newjeto) = stats(stav.baljeto, newjeto) + 1
  1986.                 game.sync_stats = 1
  1987.             END IF
  1988.             stav.baljeto = newjeto
  1989.             bala.freezedtime = -(stav.baljeto = bala.i) * FREEZE_TIME / game.fpscoef
  1990.             balb.freezedtime = -(stav.baljeto = balb.i) * FREEZE_TIME / game.fpscoef
  1991.             bala.protivnik = balb.i
  1992.             balb.protivnik = bala.i
  1993.             FOR i = 1 TO stav.pocetgulx
  1994.                 IF bal(i).i <> stav.baljeto AND bal(i).riadena = 1 THEN bal(i).protivnik = bal(stav.baljeto).i
  1995.             NEXT i
  1996.         END IF
  1997.     END IF
  1998.  
  1999. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2000. SUB PerformBounce (bala AS gula, balb AS gula) 'Calculate balls bounce
  2001.     DIM dx AS SINGLE, dy AS SINGLE, d AS SINGLE
  2002.     DIM l AS SINGLE, l1 AS SINGLE, l2 AS SINGLE
  2003.     DIM k AS SINGLE, k1 AS SINGLE, k2 AS SINGLE
  2004.     DIM m AS SINGLE, m1 AS SINGLE, m2 AS SINGLE
  2005.     DIM n AS SINGLE, n1 AS SINGLE, n2 AS SINGLE
  2006.     DIM cosa AS SINGLE, cosb AS SINGLE, p AS SINGLE
  2007.     DIM ma AS SINGLE, mb AS SINGLE
  2008.  
  2009.     IF stav.massallowed = 1 THEN
  2010.         ma = bala.m
  2011.         mb = balb.m
  2012.     ELSE
  2013.         ma = 1
  2014.         mb = 1
  2015.     END IF
  2016.  
  2017.     dx = balb.x - bala.x
  2018.     dy = balb.y - bala.y
  2019.     d = SQR(dx ^ 2 + dy ^ 2)
  2020.  
  2021.     p = bala.r + balb.r - d 'Move overlapping balls away from each other
  2022.     IF p >= 0 THEN
  2023.         bala.x = bala.x - dx * p / d / 2 * 2
  2024.         bala.y = bala.y - dy * p / d / 2 * 2
  2025.         balb.x = balb.x + dx * p / d / 2 * 2
  2026.         balb.y = balb.y + dy * p / d / 2 * 2
  2027.  
  2028.         dx = balb.x - bala.x
  2029.         dy = balb.y - bala.y
  2030.         d = SQR(dx ^ 2 + dy ^ 2)
  2031.     END IF
  2032.  
  2033.     cosa = dy / d
  2034.     cosb = -cosa
  2035.  
  2036.     l = (dx * bala.v.x * ma + dy * bala.v.y * ma) / d
  2037.     l2 = l * cosa
  2038.     k = (dy * bala.v.x * ma - dx * bala.v.y * ma) / d
  2039.     k1 = k * cosa
  2040.     l1 = bala.v.x * ma - k1
  2041.     k2 = bala.v.y * ma - l2
  2042.  
  2043.     n = (-dx * balb.v.x * mb - dy * balb.v.y * mb) / d
  2044.     n2 = n * cosb
  2045.     m = (-dy * balb.v.x * mb + dx * balb.v.y * mb) / d
  2046.     m1 = m * cosb
  2047.     n1 = balb.v.x * mb - m1
  2048.     m2 = balb.v.y * mb - n2
  2049.  
  2050.     bala.v.x = (k1 + n1) / ma
  2051.     bala.v.y = (k2 + n2) / ma
  2052.     balb.v.x = (m1 + l1) / mb
  2053.     balb.v.y = (m2 + l2) / mb
  2054.  
  2055. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2056. FUNCTION MATHROUND (n)
  2057.     MATHROUND = FIX(n + 0.5 * SGN(n))
  2058.  
  2059. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2060. SUB RotateVector (vn AS vektor, b AS SINGLE)
  2061.     DIM vt AS vektor
  2062.     sinb = SIN(b): cosb = COS(b)
  2063.     vt.x = vn.x * cosb - vn.y * sinb
  2064.     vt.y = vn.y * cosb + vn.x * sinb
  2065.     vn = vt
  2066.  
  2067. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2068. SUB CalculateFps 'calculate actual fps coeficient for correct timig
  2069.     IF game.tend = game.tstart THEN
  2070.         game.fpsactual = stav.fps
  2071.     ELSE
  2072.         game.fpsactual = ((1 / (game.tend - game.tstart)) * 1 + game.fpsactual * 99) / 100
  2073.     END IF
  2074.     game.tstart = game.tend
  2075.     game.fpscoef = stav.fps / game.fpsactual
  2076.  
  2077. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2078. SUB DisplayMessages
  2079.     IF game.msgcount > 0 THEN 'refresh messages validity
  2080.         FOR i = 1 TO game.msgcount
  2081.             IF ((TIMER(.001) - msg(i).time_start) > msg(i).time_dur) AND msg(i).time_dur > 0 THEN DeleteMessage (i)
  2082.         NEXT i
  2083.     END IF
  2084.     LOCATE 3, 1
  2085.     IF game.msgcount > 0 THEN 'print messages
  2086.         FOR i = 1 TO game.msgcount
  2087.             COLOR msg(i).col
  2088.             PRINT RTRIM$(msg(i).text)
  2089.         NEXT i
  2090.     END IF
  2091.  
  2092. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2093. SUB AddMessage (mtext AS STRING, mcol AS INTEGER, mdur AS SINGLE, mdist AS INTEGER)
  2094.     IF LEFT$(mtext, 1) = "/" THEN
  2095.         TextCommand mtext
  2096.     ELSE
  2097.         IF mdur <> 0 THEN
  2098.             IF game.msgcount >= MSG_COUNT_MAX THEN DeleteMessage (1) 'delete last message if full
  2099.             game.msgcount = game.msgcount + 1
  2100.             msg(game.msgcount).clientid = game.clientid
  2101.             msg(game.msgcount).text = mtext
  2102.             msg(game.msgcount).col = mcol
  2103.             msg(game.msgcount).time_dur = mdur
  2104.             msg(game.msgcount).time_start = TIMER(.001)
  2105.             msg(game.msgcount).syncstr = "SNM"
  2106.             msg(game.msgcount).synctime = 0
  2107.             msg(game.msgcount).distribute = mdist
  2108.             IF mdist = 1 THEN game.newmsg = 1
  2109.         END IF
  2110.     END IF
  2111.  
  2112. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2113. SUB DeleteMessage (mid AS INTEGER)
  2114.     IF mid < game.msgcount AND mid > 0 THEN
  2115.         FOR i = mid TO game.msgcount - 1
  2116.             msg(i) = msg(i + 1)
  2117.         NEXT i
  2118.     END IF
  2119.     game.msgcount = game.msgcount - 1
  2120.  
  2121. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2122. SUB TextCommand (ctext AS STRING)
  2123.     SELECT CASE LEFT$(RTRIM$(ctext), 2)
  2124.         CASE "/q" 'end network game
  2125.             IF game.hosth <> 0 AND stav.gameplay = 1 THEN
  2126.                 stav.koniec_kola = 2
  2127.                 AddMessage "The match has been ended by the host", MSG_COL, MSG_DUR_STD, 1
  2128.             END IF
  2129.             IF game.clienth <> 0 AND stav.gameplay = 1 THEN
  2130.                 stav.koniec_kola = 2
  2131.                 ResetNetGame
  2132.             END IF
  2133.         CASE "/d" 'disconnet client clidx
  2134.             clidx = VAL(MID$(RTRIM$(ctext), 3, LEN(RTRIM$(ctext)) - 2))
  2135.             IF game.hosth <> 0 AND clidx > 1 AND clidx <= PLAYER_NET_MAX THEN
  2136.                 IF client(clidx).conn <> 0 THEN
  2137.                     CloseClientConn clidx
  2138.                     AddMessage "Player " + RTRIM$(hrac(clidx).meno) + " was disconnected by host", MSG_COL, MSG_DUR_STD, 1
  2139.                 END IF
  2140.             END IF
  2141.     END SELECT
  2142.  
  2143. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2144. SUB DebugScreen (K AS INTEGER) 'print debyg information on screen in runtime
  2145.     COLOR 7
  2146.     LOCATE INT((stav.pocethracov - 0.5) / 4) + 2, 1
  2147.     PRINT "FPS:   "; game.fpsactual
  2148.  
  2149.     IF K = 101 THEN stav.energydec = -(stav.energydec = 0) * ENERGY_DEC
  2150.     IF K = 113 THEN
  2151.         bal(hrac(2).bal).strat = bal(hrac(2).bal).strat + 1
  2152.         IF bal(hrac(2).bal).strat > UBOUND(strat) THEN
  2153.             bal(hrac(2).bal).strat = 1
  2154.         END IF
  2155.     END IF
  2156.     COLOR 13
  2157.     PRINT "hrac(n)..isleft   "; klav(hrac(1).klavesy).isleft; klav(hrac(2).klavesy).isleft; klav(hrac(3).klavesy).isleft; klav(hrac(4).klavesy).isleft
  2158.     PRINT "hrac(n)..isright  "; klav(hrac(1).klavesy).isright; klav(hrac(2).klavesy).isright; klav(hrac(3).klavesy).isright; klav(hrac(4).klavesy).isright
  2159.     PRINT "hrac(n)..isdown   "; klav(hrac(1).klavesy).isdown; klav(hrac(2).klavesy).isdown; klav(hrac(3).klavesy).isdown; klav(hrac(4).klavesy).isdown
  2160.     PRINT "hrac(n)..isup     "; klav(hrac(1).klavesy).isup; klav(hrac(2).klavesy).isup; klav(hrac(3).klavesy).isup; klav(hrac(4).klavesy).isup
  2161.     PRINT "bal(2).vi        "; bal(hrac(2).bal).vi.x; bal(hrac(2).bal).vi.y
  2162.     PRINT "bal(3).vi        "; bal(hrac(3).bal).vi.x; bal(hrac(3).bal).vi.y
  2163.     PRINT "bal(4).vi        "; bal(hrac(4).bal).vi.x; bal(hrac(4).bal).vi.y
  2164.     PRINT "bal(2).strat     "; bal(2).strat
  2165.     PRINT "bal(2).klucka_time    "; bal(hrac(2).bal).klucka_time
  2166.     PRINT "bal(2).klucka_pocet   "; bal(hrac(2).bal).klucka_pocet
  2167.     PRINT "bal(2).klucka_trvanie "; bal(hrac(2).bal).klucka_trvanie
  2168.     PRINT "bal(2).klucka_uhol    "; bal(hrac(2).bal).klucka_uhol
  2169.     PRINT "bal(n).protivnik      "; bal(1).protivnik; bal(2).protivnik; bal(3).protivnik
  2170.     PRINT "bal(n).complayer      "; bal(1).protivnik; bal(2).protivnik; bal(3).protivnik
  2171.     PRINT "stav.baljeto          "; stav.baljeto
  2172.     PRINT "stav.koniec_kola      "; stav.koniec_kola
  2173.     PRINT "nearestPlayer(1)      "; nearestPlayer(bal(1), 0, 1)
  2174.     PRINT "bal(n).reactdrop_time _dur "; bal(2).reactdrop_time; bal(2).reactdrop_dur
  2175.     PRINT "bal(1).v              "; bal(1).v.x; bal(1).v.y
  2176.     PRINT "bal(1).a              "; bal(1).a.x; bal(1).a.y
  2177.     PRINT "bal(2).boostintended  "; bal(2).boostintended
  2178.     PRINT "bal(2).boost_time     "; bal(1).boost_time
  2179.     PRINT "bal(2).boost_trvanie  "; bal(1).boost_trvanie
  2180.     PRINT "bal(n).hrac           "; bal(1).hrac; bal(2).hrac; bal(3).hrac; bal(4).hrac
  2181.     PRINT "bal(1).syncstr        "; bal(1).syncstr
  2182.     PRINT "game.autostart_time   "; game.autostart_time
  2183.     PRINT "stav.autostart_set    "; stav.autostart_set
  2184.     PRINT "TIMER                 "; TIMER
  2185.     PRINT "client(n).conn"; client(1).conn; client(2).conn; client(3).conn; client(4).conn
  2186.     PRINT "hrac(n).klavesy"; hrac(1).klavesy; hrac(2).klavesy; hrac(3).klavesy; hrac(4).klavesy; hrac(10).klavesy
  2187.     PRINT "klav(hrac(n)klavesy).typ"; klav(hrac(1).klavesy).typ; klav(hrac(2).klavesy).typ; klav(hrac(3).klavesy).typ; klav(hrac(4).klavesy).typ; klav(hrac(10).klavesy).typ
  2188.     PRINT "game.sync_stats         "; game.sync_stats
  2189.  
  2190.  
  2191.  
  2192. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2193.  
  2194.  


Edit:

28.5.2019  update: fixed error while using older gamepads with less than 8 axis

25.1.2020  version 2.2
tag22.png
* tag22.png (Filesize: 15.9 KB, Dimensions: 1282x747, Views: 125)
« Last Edit: January 25, 2020, 03:29:27 pm by dajan »

Offline dajan

  • Newbie
  • Posts: 41
    • View Profile
Re: Tag! 2.1 game with LAN/internet multiplayer for max. 8 players
« Reply #1 on: April 30, 2019, 02:16:07 pm »
If you want to help me to test, there is currently a game hosted on dajan.no-ip.biz

Offline dajan

  • Newbie
  • Posts: 41
    • View Profile
Re: Tag! 2.2 - the chasing game with network multiplayer for up to 8 players
« Reply #2 on: January 25, 2020, 04:10:25 pm »
I have done some improvements to this simple chasing game in the version 2.2 of it:

- graphics polished slightly
- added energy indicator on player
- increased the player boost speed to 5 times of the normal speed (much more fun)
- AI chooses a victim to chase more carefuly now. Unless being in low energy/panic mode, it chooses to eliminate the player closest to the victory. Therefore, it is really hard to win the match to zero. Analog gamepad is strongly recommended for the best control experience.

Code: QB64: [Select]
  1. '--------constants----------------------------------------------------------------------------------------------------------------------------------
  2. CONST MENU_BUILDSTR = "2.2.20.01.01"
  3. CONST RES_X = 1280 'screen horizontal resolution, set to -1 to match the dsektop
  4. CONST RES_Y = 720 'screen vertical resolution, set to -1 to match the dsektop
  5. CONST FPS = 100 'set target frames per seconds to 100 for proper physicss speed
  6. CONST DEBUGMODE = 0 'initial state of debug mode
  7. CONST FULLSCREENMODE = 0 'set 1 for fullscreen on startup, other value to window mode
  8. CONST RES_X_TARG = 1920 'world target horizontal resolution
  9. CONST RES_Y_TARG = 1080 'world target vertical resolution
  10. CONST MAX_COL = 247 'max. color number to use of 255
  11. CONST PLAYER_COUNT_INI = 3 'Initial players count
  12. CONST POCET_GUL_INI = 20 'Initial non player balls count
  13. CONST PLAYER_COUNT_MAX = 32 'Maximum players count
  14. CONST PLAYER_NET_MAX = 8 'Max. number of network players
  15. CONST PLAYER_NET_MIN = 2 'First network control enabled player
  16. CONST POCET_GUL_MAX = 32 'Maximum balls count
  17. CONST STRAT_COUNT_MAX = 20 'Maximum players strategies
  18. CONST PLAY_BALL_R = 20 'Players ball size
  19. CONST V_MAX = 300 / FPS 'Max player ball velocity
  20. CONST ACC_X = 10 / FPS 'Balls acceleration X
  21. CONST ACC_Y = 10 / FPS 'Balls accelereation Y
  22. CONST FRICTION = 0.998 'Space fricition coeffifcient
  23. CONST ENERGY_ALL_MIN = 500 'min.starting energy of all players together
  24. CONST ENERGY_PLAYER_MIN = 50 'min. player's starting energy
  25. CONST ENERGY_PLAYER_MAX = 150 'max. player's starting energy
  26. CONST ENERGY_DEC = 5 / FPS 'ball energy decrement
  27. CONST REACTIME_AVG_NORMAL = 0 * FPS 'Average AI reaction time in sec*FPS
  28. CONST CHYBASMERU_NORMAL = 0.0 'Improper reaction rate
  29. CONST PRIESTOR_MIN = PLAY_BALL_R + 100
  30. CONST DELAY_ROUNDSTART = 8 'delay in seconds after round end to accept resume
  31. CONST DELAY_MATCHEND = 20 'delay in seconds after match end to accept resume
  32. CONST FREEZE_TIME = 1.0 * FPS 'player freeze time after getting tag in seconds
  33. CONST SCORE_POS_X = 50 'Score horizontal draw position
  34. CONST SCORE_DELTA_Y = 13 'Score draw - vertictal offset from screen top
  35. CONST SCORE_DELTA_X = 350 'Score draw - players score horizontal offset
  36. CONST SCORE_NAME_LEN = 120 'Score draw - horizontal space for player name
  37. CONST BOOST_V_KOEF = 3.0 'boost factor of extra maximum speed
  38. CONST BOOST_TRVANIE = 1 * FPS 'boost duration
  39. CONST BOOST_TIME = 1 * FPS 'min time between boosts in sec*fps
  40. CONST MENUCURSOR_COLOR = 15 'Menu cursor color
  41. CONST MENUITEM_COLOR = 7 'Menu cursor color
  42. CONST MENU_PLAYERS_COUNT = 8 'Number of players in menu
  43. CONST FONT_W = 8 'Font width in pixels
  44. CONST FONT_H = 16 'Font height in pixels
  45. CONST GAMESTICK_DEADZONE = 0.05 'gamepad stick axis maximum value considered as zero
  46. CONST CPU_CONTROLID = PLAYER_NET_MAX + 1 'CPU controller id
  47. CONST HOSTIP_DEFAULT = "" '"localhost" '
  48. CONST HOSTPORT_DEFAULT = 7432
  49. CONST GETITEM_SYNC = "SNC" 'network trasfer item code
  50. CONST GETITEM_STAV = "SNS" 'network trasfer item code
  51. CONST GETITEM_BAL = "SNB" 'network trasfer item code
  52. CONST GETITEM_PBAL = "SNP" 'network trasfer item code
  53. CONST GETITEM_KLAV = "SNK" 'network trasfer item code
  54. CONST GETITEM_HRAC = "SNH" 'network trasfer item code
  55. CONST GETITEM_MESG = "SNM" 'network trasfer item code
  56. CONST GETITEM_STAT = "SNT" 'network trasfer item code
  57. CONST NET_TIMEOUT_LIMIT = 10 'timeout to disconnect in seconds
  58. CONST NET_TIMEOUT_SYNC = 0.01 'timeout to get synced in seconds
  59. CONST MSG_COUNT_MAX = 20 'max. messages displayed
  60. CONST MSG_LEN_MAX = 80 'max. length of message
  61. CONST MSG_DUR_STD = 20 'message standard duration in seconds
  62. CONST MSG_COL = 7 'message standard color
  63. CONST UPDATE_PER_BAL = 0.1 'all balls network update period in seconds
  64. CONST UPDATE_PER_PLBAL = 0.04 'player balls network update period in seconds
  65. CONST UPDATE_PER_HRAC = 0.1 'players network update period in seconds
  66. CONST UPDATE_PER_STAV = 0.04 'world state network update period in seconds
  67. CONST AUTOSTART_SEC_MAX = 180 'max game autostart time
  68.  
  69. '--------type definitions----------------------------------------------------------------------------------------------------------------------------
  70. TYPE vektor 'general 2D vector type
  71.     x AS SINGLE
  72.     y AS SINGLE
  73.  
  74. TYPE tstav 'game world state type  (cca 100B)
  75.     minx AS INTEGER 'world - minimum x position
  76.     maxx AS INTEGER 'world - maximum x position
  77.     miny AS INTEGER 'world - minimum y position
  78.     maxy AS INTEGER 'world - maximum y position
  79.     vmax AS SINGLE 'maximum unboosted ball velocity
  80.     maxcol AS INTEGER 'world - maximum of colors
  81.     pocetgulzvoleny AS INTEGER 'number of balls desired
  82.     pocetgul AS INTEGER 'number of balls actual
  83.     pocetgulx AS INTEGER 'number of balls in bottom row
  84.     pocetguly AS INTEGER 'number of balls in left column
  85.     pocethracov AS INTEGER 'number of players
  86.     energy_allmin AS SINGLE 'all players starting energy sum
  87.     energy_playermin AS SINGLE 'minimum starting energy of player
  88.     energy_playermax AS SINGLE 'maximum starting energy of player
  89.     energy_player_start AS SINGLE 'player's starting energy
  90.     energydec AS SINGLE 'energy decrease unit
  91.     baljeto AS INTEGER 'actual tagged ball idx
  92.     balbudeto AS INTEGER 'successor tagged ball idx
  93.     vitaz AS INTEGER 'winner player idx
  94.     koniec_kola AS _BYTE 'end of round flag (0=no end, 1=end of round, 2=end fo round and match)
  95.     odpor AS SINGLE 'space friction
  96.     massallowed AS _BYTE 'calculate balls with different masses
  97.     pauza AS _BYTE 'game pause flag (0 or 1)
  98.     gameplay AS _BYTE 'state of session: 0=menu, 1=gameplay
  99.     fps AS SINGLE 'fps target set
  100.     zacinabal AS INTEGER 'initially tagged ball idx
  101.     boostallowed AS _BYTE 'player boost mode allowed (0 or 1)
  102.     syncstr AS STRING * 3 'net synchronisation string code
  103.     synctime AS DOUBLE 'net synchronisation time stamp
  104.     AIallowed AS _BYTE 'AI players in netgame allowed=1
  105.     autostart_set AS INTEGER 'game autostart setting in seconds; 0=off
  106.     autostart_timeleft AS SINGLE 'actual time to autostart the game in seconds
  107.     winscore AS _BYTE 'score to win a match
  108.  
  109. TYPE gula 'ball  type  (cca 90B)
  110.     i AS INTEGER 'balls id
  111.     x AS SINGLE 'balls y position
  112.     y AS SINGLE 'balls y position
  113.     r AS SINGLE 'radius
  114.     m AS SINGLE 'mass
  115.     v AS vektor 'velocity
  116.     vi AS vektor 'desired velocity
  117.     a AS vektor 'acceleration
  118.     col AS INTEGER 'balls color
  119.     riadena AS INTEGER 'is actually player controllable (0 or 1)
  120.     complayer AS INTEGER 'is computer player's ball (0 or 1)
  121.     protivnik AS INTEGER 'actual ball's enemy ball id
  122.     strat AS INTEGER 'balls behaviour strategy id
  123.     energia AS SINGLE 'players energy
  124.     freezedtime AS INTEGER 'actual time to unfreeze players control
  125.     smer_odchyl AS SINGLE 'actual deviance from intended direction in radians
  126.     klucka_time AS INTEGER 'actual time to start doging
  127.     klucka_uhol AS SINGLE 'actual dodge angle
  128.     klucka_trvanie AS INTEGER 'actual dodge duration
  129.     klucka_pocet AS INTEGER 'actual dodge count
  130.     boost_time AS INTEGER 'actual time to boost allowed again
  131.     boost_trvanie AS INTEGER 'actual time to end boost
  132.     reactdrop_time AS INTEGER 'actual time to start reaction drop in frames
  133.     reactdrop_dur AS INTEGER 'actual time to stop reaction drop in frames
  134.     boostintended AS INTEGER 'intention to boost or not (1, 0)
  135.     syncstr AS STRING * 3 'net synchronisation string code
  136.     synctime AS DOUBLE 'net synchronisation time stamp
  137.     hrac AS INTEGER 'ball player's id
  138.  
  139. TYPE tstrat 'balls behaviour strategy type
  140.     okraj AS INTEGER 'distance from screen border to turn
  141.     okraj_rot AS SINGLE 'distance from screen border to turn
  142.     chybasmeru AS SINGLE 'percentage of deviance from desired direction -- not used currently
  143.     react_lag AS INTEGER 'average reaction time in frames
  144.     klucka_avgtm AS SINGLE 'average time between dodging in fps +-50%
  145.     klucka_uhol AS SINGLE 'average dodge angle
  146.     klucka_trvanie AS INTEGER 'average dodge time +-50%
  147.     klucka_pocet AS INTEGER 'average dodge count
  148.     klucka_uholodch AS SINGLE 'dodge angle deviation percentage
  149.     detekcia_sin AS SINGLE 'sinus of frontal enemy detection angle
  150.     detekcia_vzdfull AS SINGLE 'distance of frontal enemy full detection
  151.     detekcia_vzdmin AS SINGLE 'distance of frontal enemy minimum detection
  152.     detekcia_rot AS SINGLE 'amount of rotation on enemy detection in radians
  153.     reactdrop_per AS INTEGER 'average reaction drop period in frames
  154.     reactdrop_dur AS INTEGER 'average reaction drop duration in frames
  155.     boost_rate AS SINGLE 'inclination to use boost in percent
  156.     boost_dist AS SINGLE 'enemy distance activating boost deciding
  157.     boost_chasesin AS SINGLE 'max. sinus of enemy to player velocity angle to apply chasing boost
  158.     mate_cosenmax AS SINGLE 'max. cosinus of (player-enemy vector to player velocity) to rotate to mate
  159.     mate_rot AS SINGLE 'amount of unit rotation towards nearest mate in rad per frame
  160.     mate_en_distmin AS SINGLE 'min. distance from enemy to apply rotation towards mate
  161.     mate_distmin AS SINGLE 'min. distance from mate to apply rotation towards mate
  162.     adrenalin_rate AS SINGLE 'percentage of reaction time improved in adrenalin mode
  163.     adrenalin_energy AS SINGLE 'energy level precentage starting adrenalin mode
  164.  
  165. TYPE thrac 'player type
  166.     i AS INTEGER 'player index number
  167.     meno AS STRING * 10 'players name
  168.     skore AS _BYTE 'players score
  169.     klavesy AS _BYTE 'players control keys set index
  170.     bal AS INTEGER 'player driven ball idx
  171.     col AS INTEGER 'player's choosed color
  172.     clientid AS _BYTE 'index of hosted client, 0 if local player
  173.     isready AS _BYTE '1=player is ready for game start
  174.     syncstr AS STRING * 3
  175.     synctime AS DOUBLE
  176.  
  177. TYPE tklavesy 'players controller type
  178.     id AS INTEGER 'controllers id
  179.     devid AS INTEGER 'controllers device id  (0=unused)
  180.     typ AS _BYTE 'type of controller (0=unused, 1=keyboard, 2=mouse reserved, 3=gamepad, 4=AI, 5=network, 6=empty)
  181.     nazov AS STRING * 25 'game controllers name
  182.     up AS LONG 'key code for up
  183.     down AS LONG 'keyp code for down
  184.     right AS LONG 'key code for right
  185.     left AS LONG 'key code for left
  186.     shoota AS LONG 'keyborad key code or gamepad button id
  187.     shootb AS LONG 'keyborad key code or gamepad button id
  188.     xaxis AS _BYTE 'gamepad analog x-asis id
  189.     yaxis AS _BYTE 'gamepad analog y-asis id
  190.     dxaxis AS _BYTE 'gamepad digital x-asis id
  191.     dyaxis AS _BYTE 'gamepad digital y-asis id
  192.     isup AS _BYTE 'controllers actual state for up
  193.     isdown AS _BYTE 'controllers actual state for down
  194.     isright AS _BYTE 'controllers actual state for right
  195.     isleft AS _BYTE 'controllers actual state for left
  196.     isshoota AS _BYTE 'controllers actual state for fire 1
  197.     isshootb AS _BYTE 'controllers actual state for fire 2
  198.     gvektor AS vektor 'controllers actual x and y axis state
  199.     buttonstate AS LONG 'all gamepads buttons cumulative state value
  200.     buttonstatechanged AS _BYTE '0 if no button state change, 1 if any button state changed
  201.     buttonstatetimer AS INTEGER
  202.     syncstr AS STRING * 3 'net synchronisation string code
  203.     synctime AS DOUBLE 'net synchronisation time stamp
  204.  
  205. TYPE tmenu
  206.     title AS STRING * 10 'menu title use for nothing
  207.     items_count AS INTEGER 'menu items count
  208.     item_active AS INTEGER 'active item index
  209.     item_height AS INTEGER 'menu item height in pixels
  210.     cursor_col AS INTEGER 'menu cursor color
  211.     cursor_xpos AS INTEGER 'x position of cursors left side
  212.     cursor_out AS INTEGER 'cursor bar outset
  213.     ypos AS INTEGER 'y position of cursor
  214.     cursor_width AS INTEGER 'cursor width in pixels
  215.     menucode AS INTEGER 'stores code of selected action upon active menuitem
  216.     playercode AS INTEGER 'stores  player id of active menuitem
  217.  
  218. TYPE tmessage
  219.     clientid AS INTEGER 'client origin of message
  220.     text AS STRING * MSG_LEN_MAX 'message text
  221.     time_start AS DOUBLE 'message time
  222.     time_dur AS SINGLE 'message duration
  223.     col AS INTEGER 'message color
  224.     distribute AS INTEGER 'message dsitribution status 1=distribute
  225.     syncstr AS STRING * 3 'net synchronisation string code
  226.     synctime AS DOUBLE 'net synchronisation time stamp
  227.  
  228. TYPE tgamesession 'my game session properties
  229.     maxx AS INTEGER 'my screen x resolution
  230.     maxy AS INTEGER 'my screen y resolution
  231.     isnetgame AS _BYTE 'network game flag; 1=network, 0=local
  232.     hosth AS INTEGER 'my host handle
  233.     clienth AS INTEGER 'my client handle
  234.     clientid AS INTEGER 'my client's id registered at host
  235.     host_ip AS STRING * 100 'host's ip to connect as a client
  236.     host_port AS LONG 'host's port to connect as a client
  237.     get_item AS STRING * 3 'actual item to get from host
  238.     get_time AS DOUBLE 'time of last succesfull reading from host
  239.     get_isdesync AS INTEGER 'net transfer is desynced=1
  240.     fpsactual AS SINGLE 'fps achieved
  241.     fpscoef AS SINGLE 'fps actual/target
  242.     tstart AS DOUBLE 'timer start time
  243.     tend AS DOUBLE 'timer end time
  244.     pocetgamepadov AS INTEGER 'number of gamepads detected
  245.     pocetkontrolerov AS INTEGER 'number of game controllers
  246.     start AS INTEGER 'start game flag (0 or 1)
  247.     exituj AS INTEGER 'exit game flag (0 or 1)
  248.     printdebug AS INTEGER 'debug mode flag (0 or 1)
  249.     msgcount AS INTEGER 'total number of actual messages
  250.     msgwrite AS INTEGER 'message write mode=1
  251.     newmsg AS INTEGER 'is new messages to distribute flag
  252.     msgtext AS STRING * MSG_LEN_MAX 'message text being actually written
  253.     autostart_time AS SINGLE 'local host time of game autostart
  254.     timer_pballs AS SINGLE 'local time of last player balls update from host
  255.     sync_stats AS _BYTE 'send statistics to clients flag
  256.     show_stats AS _BYTE 'display match statistics
  257.  
  258. TYPE tclient 'hosted client type
  259.     conn AS INTEGER 'connection handle of hosted client
  260.     ip AS STRING * 100 'ip of hosted client
  261.     get_item AS STRING * 3 'next item to get from client
  262.     get_time AS DOUBLE 'time of last succesfull reading from client
  263.     get_isdesync AS INTEGER 'net desynchronisation flag host cannot get from client
  264.     isdesynced AS _BYTE 'client side is desynced - cannot get from host
  265.     spectating AS _BYTE 'client player control enabled
  266.  
  267. TYPE tsyncvar
  268.     syncstr AS STRING * 3 'sync string code
  269.     synctime AS DOUBLE 'net synchronisation time stamp
  270.     itemstr AS STRING * 3 'code of the next item to get
  271.     isdesync AS _BYTE '1=the other side is desynced
  272.     clientid AS INTEGER 'client id
  273.  
  274. '-----global variables-------------------------------------------------------------------------------------------------------------------------------
  275. REDIM SHARED bal(1 TO POCET_GUL_MAX + PLAYER_COUNT_MAX) AS gula 'balls
  276. DIM SHARED klav(1 TO PLAYER_COUNT_MAX) AS tklavesy 'keyboard control sets
  277. DIM SHARED stav AS tstav 'game world state
  278. DIM SHARED strat(1 TO STRAT_COUNT_MAX) AS tstrat 'computer players strategies
  279. DIM SHARED hrac(1 TO PLAYER_COUNT_MAX) AS thrac 'players balls
  280. DIM SHARED sync AS tsyncvar 'net sync variable
  281. DIM SHARED stats(1 TO PLAYER_NET_MAX + 1, 1 TO PLAYER_NET_MAX) AS INTEGER 'players game statistics
  282.  
  283. REDIM SHARED ibal(1 TO POCET_GUL_MAX) AS gula 'nonplayer balls transfer array
  284. REDIM SHARED ipbal(1 TO PLAYER_NET_MAX) AS gula 'players balls transfer array
  285. DIM SHARED iklav(1 TO PLAYER_COUNT_MAX) AS tklavesy 'keyboard control sets transfer array
  286. DIM SHARED istav AS tstav 'game world state transfer array
  287. DIM SHARED ihrac(1 TO PLAYER_NET_MAX) AS thrac 'players balls transfer array
  288. DIM SHARED isync AS tsyncvar 'net sync variable transfer array
  289. DIM SHARED istats(1 TO PLAYER_NET_MAX + 1, 1 TO PLAYER_NET_MAX) AS INTEGER 'players game statistics transfer
  290.  
  291. DIM SHARED client(1 TO 8) AS tclient
  292. DIM SHARED menu AS tmenu
  293. DIM SHARED game AS tgamesession
  294. DIM SHARED msg(1 TO MSG_COUNT_MAX) AS tmessage
  295.  
  296. '----main program------------------------------------------------------------------------------------------------------------------------------------
  297. SetGlobalParameters
  298. SetUpControllers
  299. IF game.pocetgamepadov > 0 THEN hrac(1).klavesy = game.pocetkontrolerov 'set gamepad as default controller for player1
  300.  
  301. SCREEN _NEWIMAGE(game.maxx, game.maxy, 256), , ,
  302. IF FULLSCREENMODE = 1 THEN _FULLSCREEN
  303. AddMessage "Welcome in the Tag game.", MSG_COL, 5, 0
  304. AddMessage "To display/hide match statistics press 'c' while gameplay.", MSG_COL, 8, 0
  305. AddMessage "Default TCP port for the network game is 7432. ", MSG_COL, 11, 0
  306.  
  307.     '--------Main menu begin---------------------------------------------------------------------------------------------------------------------------------------------------------------------
  308.     REDIM SHARED ibal(1 TO POCET_GUL_MAX) AS gula 'nonplayer balls transfer array
  309.     REDIM SHARED ipbal(1 TO PLAYER_NET_MAX) AS gula 'players balls transfer array
  310.     DIM playerst(1 TO 8) AS STRING * 30
  311.     DIM hoststat AS STRING * 50
  312.     DIM joinstat AS STRING * 50
  313.     DIM autostart AS STRING
  314.     DIM AIplayers AS STRING
  315.     DIM newenergy AS STRING * 3
  316.  
  317.     stav.gameplay = 0
  318.     vys = FONT_H
  319.     sir = FONT_W
  320.     menu.items_count = 27
  321.  
  322.     DO
  323.         WINDOW (0, 0)-(game.maxx, game.maxy)
  324.         IF game.hosth <> 0 THEN hoststat = "host started" ELSE hoststat = ""
  325.         IF game.clienth <> 0 AND game.clientid = 0 THEN joinstat = "joining game...   "
  326.         IF game.clienth <> 0 AND game.clientid <> 0 THEN joinstat = "connected to         " + LEFT$(game.host_ip, 30)
  327.         IF game.clienth = 0 AND game.clientid = 0 THEN joinstat = ""
  328.         ReadyPlayersCount = 0
  329.         FOR i = 1 TO 8
  330.             playerst(i) = ""
  331.             IF game.isnetgame = 1 THEN
  332.                 IF hrac(i).clientid > 0 THEN playerst(i) = "  state: connected" ELSE playerst(i) = "  state: "
  333.                 IF klav(hrac(i).klavesy).typ <= 3 THEN playerst(i) = "  state: local player"
  334.                 IF hrac(i).isready = 1 AND hrac(i).clientid > 0 THEN playerst(i) = RTRIM$(playerst(i)) + " / ready!": ReadyPlayersCount = ReadyPlayersCount + 1
  335.             END IF
  336.         NEXT i
  337.  
  338.         IF stav.autostart_set = 0 THEN
  339.             autostart = "Off"
  340.         ELSE
  341.             HumanPlayers = HumanPlayersCount
  342.             IF game.autostart_time - stav.autostart_set > TIMER THEN game.autostart_time = game.autostart_time - 86399
  343.             IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) THEN stav.autostart_timeleft = game.autostart_time - TIMER
  344.             IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) AND game.autostart_time <= TIMER THEN
  345.                 IF stav.AIallowed = 1 OR (stav.AIallowed = 0 AND HumanPlayers >= 2) THEN
  346.                     game.start = 1
  347.                 ELSE
  348.                     game.autostart_time = TIMER + stav.autostart_set
  349.                 END IF
  350.             END IF
  351.             IF game.isnetgame = 1 AND game.hosth <> 0 AND ReadyPlayersCount = HumanPlayers AND ((stav.AIallowed = 1 AND HumanPlayers >= 1) OR (stav.AIallowed = 0 AND HumanPlayers >= 2)) THEN game.start = 1
  352.             autostart = LTRIM$(STR$(stav.autostart_set)) + " sec               " + LTRIM$(STR$(INT(stav.autostart_timeleft))) + " left"
  353.         END IF
  354.  
  355.         IF stav.AIallowed = 1 THEN AIplayers = "Yes" ELSE AIplayers = "No"
  356.  
  357.         CLS 1
  358.         DisplayMessages
  359.         PrintMenuItem -2, menu, 0, 0, 0, "Tag! v.2", 0, 0, -3, 13
  360.         PrintMenuItem -1, menu, 0, 0, 0, "created in QuickBasic64 by Daniel Janec (2019) build " + MENU_BUILDSTR, 0, 0, -2, 20
  361.         PrintMenuItem 1, menu, 6, 0, 0, "Play <Space>", 0, 0, 0, MENUITEM_COLOR
  362.         PrintMenuItem 2, menu, 1, 0, 0, "Refresh gamepads detected: " + STR$(game.pocetgamepadov), 0, 0, 0, MENUITEM_COLOR
  363.         PrintMenuItem 3, menu, 2, 1, 1, hrac(1).meno + " controls: " + klav(hrac(1).klavesy).nazov + RTRIM$(playerst(1)), 150, -10 * sir, 1, hrac(1).col
  364.         PrintMenuItem 4, menu, 3, 1, 1, "              color:" + STR$(hrac(1).col), 150, -10 * sir, 1, hrac(1).col
  365.         PrintMenuItem 5, menu, 2, 2, 1, hrac(2).meno + " controls: " + klav(hrac(2).klavesy).nazov + RTRIM$(playerst(2)), 150, -10 * sir, 1, hrac(2).col
  366.         PrintMenuItem 6, menu, 3, 2, 1, "              color:" + STR$(hrac(2).col), 150, -10 * sir, 1, hrac(2).col
  367.         PrintMenuItem 7, menu, 2, 3, 1, hrac(3).meno + " controls: " + klav(hrac(3).klavesy).nazov + RTRIM$(playerst(3)), 150, -10 * sir, 1, hrac(3).col
  368.         PrintMenuItem 8, menu, 3, 3, 1, "              color:" + STR$(hrac(3).col), 150, -10 * sir, 1, hrac(3).col
  369.         PrintMenuItem 9, menu, 2, 4, 1, hrac(4).meno + " controls: " + klav(hrac(4).klavesy).nazov + RTRIM$(playerst(4)), 150, -10 * sir, 1, hrac(4).col
  370.         PrintMenuItem 10, menu, 3, 4, 1, "              color:" + STR$(hrac(4).col), 150, -10 * sir, 1, hrac(4).col
  371.         PrintMenuItem 11, menu, 2, 5, 1, hrac(5).meno + " controls: " + klav(hrac(5).klavesy).nazov + RTRIM$(playerst(5)), 150, -10 * sir, 1, hrac(5).col
  372.         PrintMenuItem 12, menu, 3, 5, 1, "              color:" + STR$(hrac(5).col), 150, -10 * sir, 1, hrac(5).col
  373.         PrintMenuItem 13, menu, 2, 6, 1, hrac(6).meno + " controls: " + klav(hrac(6).klavesy).nazov + RTRIM$(playerst(6)), 150, -10 * sir, 1, hrac(6).col
  374.         PrintMenuItem 14, menu, 3, 6, 1, "              color:" + STR$(hrac(6).col), 150, -10 * sir, 1, hrac(6).col
  375.         PrintMenuItem 15, menu, 2, 7, 1, hrac(7).meno + " controls: " + klav(hrac(7).klavesy).nazov + RTRIM$(playerst(7)), 150, -10 * sir, 1, hrac(7).col
  376.         PrintMenuItem 16, menu, 3, 7, 1, "              color:" + STR$(hrac(7).col), 150, -10 * sir, 1, hrac(7).col
  377.         PrintMenuItem 17, menu, 2, 8, 1, hrac(8).meno + " controls: " + klav(hrac(8).klavesy).nazov + RTRIM$(playerst(8)), 150, -10 * sir, 1, hrac(8).col
  378.         PrintMenuItem 18, menu, 3, 8, 1, "              color:" + STR$(hrac(8).col), 150, -10 * sir, 1, hrac(8).col
  379.         PrintMenuItem 19, menu, 8, 0, 1, "Player starting energy: " + LTRIM$(STR$(stav.energy_player_start)), 100, -10 * sir, 2, MENUITEM_COLOR
  380.         PrintMenuItem 20, menu, 4, 0, 1, "     Number of players: " + LTRIM$(STR$(stav.pocethracov)), 100, -10 * sir, 2, MENUITEM_COLOR
  381.         PrintMenuItem 21, menu, 12, 0, 1, "   CPU players allowed: " + AIplayers, 100, -10 * sir, 2, MENUITEM_COLOR
  382.         PrintMenuItem 22, menu, 5, 0, 1, "           Balls count: " + LTRIM$(STR$(stav.pocetgulzvoleny)), 100, -10 * sir, 2, MENUITEM_COLOR
  383.         PrintMenuItem 23, menu, 13, 0, 1, "   Match winning score: " + LTRIM$(STR$(stav.winscore)), 100, -10 * sir, 2, MENUITEM_COLOR
  384.         PrintMenuItem 24, menu, 11, 0, 1, "        Game autostart: " + autostart, 100, -10 * sir, 2, MENUITEM_COLOR
  385.         PrintMenuItem 25, menu, 9, 0, 0, "             Host game: " + hoststat, 100, -10 * sir, 2, MENUITEM_COLOR
  386.         PrintMenuItem 26, menu, 10, 0, 0, "             Join game: " + joinstat, 100, -10 * sir, 2, MENUITEM_COLOR
  387.         PrintMenuItem 27, menu, 7, 0, 0, "Exit <Esc>", 0, 0, 2, MENUITEM_COLOR
  388.         PrintMenuCursor menu
  389.  
  390.         k = _KEYHIT 'read menu action
  391.         IF game.msgwrite = 1 AND game.isnetgame = 1 THEN WriteMessage k: k = 0
  392.         IF game.msgwrite = 0 AND game.isnetgame = 1 THEN WriteMessage -1
  393.  
  394.         '---debug code begin---------------------------------------------
  395.         IF k = 98 THEN game.printdebug = -(game.printdebug = 0)
  396.         IF game.printdebug = 1 THEN DebugScreen k 'print debug info on screen
  397.         '---debug code end-----------------------------------------------
  398.  
  399.         FOR i = 1 TO game.pocetkontrolerov
  400.             IF klav(i).typ = 3 THEN
  401.                 ReadGamepad klav(i)
  402.                 IF klav(i).buttonstatechanged THEN
  403.                     IF klav(i).isshoota THEN k = 13: klav(i).isshoota = 0
  404.                     IF klav(i).isup THEN k = 18432: klav(i).isup = 0
  405.                     IF klav(i).isdown THEN k = 20480: klav(i).isdown = 0
  406.                     IF klav(i).isright THEN k = 19712: klav(i).isright = 0
  407.                     IF klav(i).isleft THEN k = 19200: klav(i).isleft = 0
  408.                 END IF
  409.             END IF
  410.         NEXT i
  411.  
  412.         SELECT CASE k 'perform menu choice
  413.             CASE 18432 'up
  414.                 IF menu.item_active > 1 THEN menu.item_active = menu.item_active - 1
  415.             CASE 20480 'down
  416.                 IF menu.item_active < menu.items_count THEN menu.item_active = menu.item_active + 1
  417.             CASE 19200 'left
  418.                 SELECT CASE menu.menucode
  419.                     CASE 2 'players controller
  420.                         i = menu.playercode
  421.                         hrac(i).klavesy = hrac(i).klavesy - 1
  422.                         IF hrac(i).klavesy < PLAYER_NET_MAX + 1 THEN hrac(i).klavesy = PLAYER_NET_MAX + 1
  423.                     CASE 3 'players color
  424.                         i = menu.playercode
  425.                         hrac(i).col = hrac(i).col - 1
  426.                         IF hrac(i).col < 1 THEN hrac(i).col = stav.maxcol
  427.                     CASE 4 'players count
  428.                         IF stav.pocethracov > 2 THEN
  429.                             stav.pocethracov = stav.pocethracov - 1
  430.                         END IF
  431.                     CASE 5 'balls count
  432.                         IF stav.pocetgulzvoleny > 0 THEN
  433.                             stav.pocetgulzvoleny = stav.pocetgulzvoleny - 1
  434.                         END IF
  435.                     CASE 8 'starting energy
  436.                         IF stav.energy_player_start > 10 THEN
  437.                             stav.energy_player_start = stav.energy_player_start - 1
  438.                         END IF
  439.                     CASE 11 'game autostart
  440.                         IF stav.autostart_set > 0 THEN
  441.                             stav.autostart_set = stav.autostart_set - 10
  442.                             game.autostart_time = TIMER + stav.autostart_set
  443.                         END IF
  444.                     CASE 12 'CPU players allowed
  445.                         stav.AIallowed = 0
  446.                     CASE 13 'Winning score
  447.                         IF stav.winscore > 1 THEN stav.winscore = stav.winscore - 1
  448.                 END SELECT
  449.             CASE 19712 'right
  450.                 SELECT CASE menu.menucode
  451.                     CASE 2 'players controller
  452.                         i = menu.playercode
  453.                         hrac(i).klavesy = hrac(i).klavesy + 1
  454.                         IF hrac(i).klavesy > game.pocetkontrolerov THEN hrac(i).klavesy = game.pocetkontrolerov
  455.                     CASE 3 'players color
  456.                         i = menu.playercode
  457.                         hrac(i).col = hrac(i).col + 1
  458.                         IF hrac(i).col > stav.maxcol THEN hrac(i).col = 1
  459.                     CASE 4 'players count
  460.                         IF stav.pocethracov < PLAYER_COUNT_MAX THEN
  461.                             stav.pocethracov = stav.pocethracov + 1
  462.                         END IF
  463.                     CASE 5 'balls count
  464.                         IF stav.pocetgulzvoleny < POCET_GUL_MAX THEN
  465.                             stav.pocetgulzvoleny = stav.pocetgulzvoleny + 1
  466.                         END IF
  467.                     CASE 8 'starting energy
  468.                         IF stav.energy_player_start < ENERGY_PLAYER_MAX THEN
  469.                             stav.energy_player_start = stav.energy_player_start + 1
  470.                         END IF
  471.                     CASE 11 'game autostart
  472.                         IF stav.autostart_set < AUTOSTART_SEC_MAX THEN
  473.                             stav.autostart_set = stav.autostart_set + 10
  474.                             game.autostart_time = TIMER + stav.autostart_set
  475.                         END IF
  476.                     CASE 12 'CPU players allowed
  477.                         stav.AIallowed = 1
  478.                     CASE 13 'Winning score
  479.                         IF stav.winscore < 99 THEN stav.winscore = stav.winscore + 1
  480.                 END SELECT
  481.             CASE 13 'enter
  482.                 SELECT CASE menu.menucode
  483.                     CASE 1
  484.                         SetUpControllers
  485.                     CASE 8 'starting energy
  486.                         newenergy = LTRIM$(STR$(stav.energy_player_start))
  487.                         newenergy = InputText$(newenergy, menu.cursor_xpos + 2 * sir, menu.ypos + (menu.item_active - 1) * menu.item_height, MENUITEM_COLOR, 48, "<Type starting energy>", 0, 1)
  488.                         IF LEFT$(newenergy$, 1) <> CHR$(27) AND LEN(RTRIM$(newenergy$)) > 0 THEN
  489.                             energyval = VAL(newenergy)
  490.                             IF energyval < 10 THEN energyval = 10
  491.                             IF energyval > ENERGY_PLAYER_MAX THEN energyval = ENERGY_PLAYER_MAX
  492.                             stav.energy_player_start = energyval
  493.                         END IF
  494.                     CASE 2 'set new name
  495.                         novemeno$ = InputText$(hrac(menu.playercode).meno, menu.cursor_xpos + 2 * sir, menu.ypos + (menu.item_active - 1) * menu.item_height, hrac(menu.playercode).col, 14, "<Type name>", 0, 0)
  496.                         IF novemeno$ <> CHR$(27) AND LEN(novemeno$) > 0 THEN hrac(menu.playercode).meno = novemeno$
  497.                     CASE 9 'host game
  498.                         HostGame
  499.                     CASE 10 'join game
  500.                         IF game.clienth = 0 THEN
  501.                             gethostip$ = InputText$(game.host_ip, menu.cursor_xpos + 2 * sir, menu.ypos + (menu.item_active - 1) * menu.item_height, MENUITEM_COLOR, LEN(game.host_ip), "<Type IP address or host name>", 1, 0)
  502.                             IF gethostip$ <> CHR$(27) THEN game.host_ip = gethostip$
  503.                             IF gethostip$ <> CHR$(27) AND LEN(RTRIM$(game.host_ip)) > 0 THEN JoinGame
  504.                         ELSE
  505.                             ResetNetGame
  506.                         END IF
  507.                     CASE 6 'start game
  508.                         IF game.clientid > 0 THEN hrac(game.clientid).isready = 1 - hrac(game.clientid).isready
  509.                         IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) AND (stav.AIallowed = 1 OR (stav.AIallowed = 0 AND HumanPlayersCount >= 2)) THEN game.start = 1
  510.                     CASE 7 'exit game
  511.                         game.exituj = 1
  512.                 END SELECT
  513.             CASE 116 'messaage write mode on
  514.                 IF game.isnetgame = 1 THEN
  515.                     game.msgwrite = 1
  516.                     k = 0
  517.                 END IF
  518.             CASE 32 'start game
  519.                 IF game.clientid > 0 THEN hrac(game.clientid).isready = 1 - hrac(game.clientid).isready
  520.                 IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) AND (stav.AIallowed = 1 OR (stav.AIallowed = 0 AND HumanPlayersCount >= 2)) THEN game.start = 1
  521.             CASE 27 'exit game
  522.                 game.exituj = 1
  523.         END SELECT
  524.  
  525.         FOR i = 1 TO PLAYER_COUNT_MAX 'set AI controller type for computer players
  526.             IF game.isnetgame = 1 AND game.clienth <> 0 AND game.clientid <> 0 AND klav(hrac(i).klavesy).typ = 4 THEN hrac(i).klavesy = hrac(i).klavesy + 1 'change AI controler on client to keyboard
  527.             IF (game.isnetgame = 0 OR (game.isnetgame = 1 AND game.hosth <> 0)) AND klav(hrac(i).klavesy).typ = 4 THEN 'on host or singleplayer change AI controlled ball
  528.                 bal(hrac(i).bal).complayer = 1
  529.             ELSE
  530.                 bal(hrac(i).bal).complayer = 0
  531.             END IF
  532.         NEXT i
  533.  
  534.         IF game.isnetgame AND stav.pocethracov > PLAYER_NET_MAX THEN stav.pocethracov = PLAYER_NET_MAX
  535.         IF menu.menucode = 4 AND (k = 19712 OR k = 19200) THEN 'after changing players count
  536.             stav.energy_player_start = INT(stav.energy_allmin / stav.pocethracov) 'calculate  players energy
  537.             IF stav.energy_player_start < stav.energy_playermin THEN stav.energy_player_start = stav.energy_playermin
  538.             IF stav.energy_player_start > stav.energy_playermax THEN stav.energy_player_start = stav.energy_playermax
  539.         END IF
  540.  
  541.         IF game.isnetgame = 1 AND game.clienth <> 0 AND game.clientid > 0 AND stav.gameplay = 1 THEN game.start = 1
  542.         IF game.isnetgame = 1 THEN SyncNetGameData
  543.         _DISPLAY
  544.         _LIMIT stav.fps
  545.  
  546.     LOOP UNTIL game.start = 1 OR game.exituj = 1
  547.     game.start = 0
  548.     IF game.clientid > 0 THEN hrac(game.clientid).isready = 0
  549.     '--------end of main menu---------------------------------------------------------------------------------------------------------------------------------------------------------------------
  550.  
  551.     '----------------------------------new game start-------------------------------------------------------------------------------------------------
  552.     WINDOW (0, 0)-(stav.maxx, stav.maxy)
  553.     IF game.exituj <> 1 THEN 'new game starts here
  554.         '----------------------------------set the world on the new game start-------------------------------
  555.         IF game.hosth <> 0 THEN AddMessage "The match has been started by the host. Whoever scores" + RTRIM$(STR$(stav.winscore)) + " times wins.", MSG_COL, MSG_DUR_STD, 1
  556.  
  557.         FOR i = 1 TO PLAYER_NET_MAX
  558.             FOR j = 1 TO PLAYER_NET_MAX
  559.                 stats(i, j) = 0
  560.             NEXT j
  561.         NEXT i
  562.  
  563.         stav.gameplay = 1
  564.         stav.pocetgul = stav.pocetgulzvoleny + stav.pocethracov
  565.         stav.pocetgulx = MATHROUND(SQR(4 / 3 * stav.pocetgul))
  566.         IF stav.pocetgulx < stav.pocethracov THEN stav.pocetgulx = stav.pocethracov
  567.         stav.pocetguly = _CEIL(stav.pocetgul / stav.pocetgulx)
  568.  
  569.         IF stav.pocethracov < 2 THEN stav.pocethracov = 2
  570.         FOR i = 1 TO stav.pocethracov
  571.             hrac(i).skore = 0
  572.         NEXT i
  573.  
  574.         FOR n = 1 TO stav.pocethracov: bal(n).syncstr = GETITEM_PBAL: bal(n).synctime = 0: NEXT n
  575.         FOR n = stav.pocethracov + 1 TO UBOUND(bal): bal(n).syncstr = GETITEM_BAL: bal(n).synctime = 0: NEXT n
  576.  
  577.         stav.zacinabal = hrac(INT(RND * stav.pocethracov + 1)).bal
  578.  
  579.         DO 'new round starts here
  580.             '-------------------------------set the world on the new round start---------------------------
  581.             stav.koniec_kola = 0
  582.             stav.pauza = 0
  583.             stav.vitaz = 0
  584.             game.exituj = 0
  585.             stav.baljeto = 0
  586.  
  587.             IF game.isnetgame = 1 AND game.hosth <> 0 THEN 'engage new spectating players
  588.                 FOR i = 2 TO stav.pocethracov
  589.                     IF client(i).spectating = 1 THEN
  590.                         hrac(i).skore = 0
  591.                         bal(hrac(i).bal).hrac = i
  592.                         bal(hrac(i).bal).complayer = 0
  593.                         bal(hrac(i).bal).riadena = 1
  594.                         client(i).spectating = 0
  595.                         bal(i).vi.x = 0: bal(i).vi.y = 0
  596.                         FOR j = 1 TO PLAYER_NET_MAX
  597.                             stats(i, j) = 0
  598.                             stats(j, i) = 0
  599.                         NEXT j
  600.                     END IF
  601.                 NEXT i
  602.             END IF
  603.  
  604.             RANDOMIZE TIMER
  605.             coloffset = 104 + INT(RND * 10)
  606.             colcoef = (1 + INT(RND * (246 - coloffset))) / stav.pocetgulx
  607.             FOR i = 1 TO stav.pocetguly + 1 'set all balls
  608.                 FOR j = 1 TO stav.pocetgulx
  609.                     n = (i - 1) * stav.pocetgulx + j:
  610.                     IF n <= stav.pocetgul THEN
  611.                         ry = 20 + RND * INT(((stav.maxy - 100) / (stav.pocetguly + 1) - 25) / 2)
  612.                         rx = 10 + RND * INT(((stav.maxx - 100) / (stav.pocetgulx + 1) - 25) / 2)
  613.                         bcol = coloffset + j * colcoef
  614.                         bal(n).i = n
  615.                         bal(n).col = bcol
  616.                         bal(n).x = 100 + j * INT((stav.maxx - 100) / (stav.pocetgulx + 1))
  617.                         bal(n).y = 100 + i * INT((stav.maxy - 100) / (stav.pocetguly + 1))
  618.                         bal(n).r = rx * -(rx < ry) + ry * -(ry < rx)
  619.                         bal(n).m = 4 / 4 * pi * bal(n).r ^ 2
  620.                         bal(n).v.x = -10 / FPS: bal(n).v.y = -30 / FPS
  621.                         bal(n).a.x = ACC_X * stav.maxx / RES_X_TARG
  622.                         bal(n).a.y = ACC_Y * stav.maxx / RES_X_TARG
  623.                         bal(n).vi.x = -(bal(n).complayer = 1) * RND * stav.vmax - stav.vmax / 2
  624.                         bal(n).vi.y = -(bal(n).complayer = 1) * RND * stav.vmax - stav.vmax / 2
  625.                         bal(n).riadena = 0
  626.                         bal(n).energia = stav.energy_player_start
  627.                         bal(n).freezedtime = 0: bal(n).klucka_time = 0: bal(n).klucka_pocet = 0
  628.                         bal(n).klucka_trvanie = 0: bal(n).klucka_uhol = 0
  629.                         bal(n).boost_time = 0: bal(n).boost_trvanie = 0
  630.                         bal(n).synctime = 0
  631.                     END IF
  632.                 NEXT j
  633.             NEXT i
  634.  
  635.             '---set players balls default atributtes--------------------------------------------------------
  636.             FOR i = 1 TO stav.pocethracov
  637.                 hrac(i).bal = i
  638.                 bal(hrac(i).bal).hrac = i
  639.                 bal(hrac(i).bal).riadena = 1
  640.                 IF stav.AIallowed = 0 AND klav(hrac(i).klavesy).typ = 4 THEN
  641.                     bal(hrac(i).bal).energia = 0
  642.                     bal(hrac(i).bal).riadena = 0
  643.                 ELSE
  644.                     bal(hrac(i).bal).r = PLAY_BALL_R * stav.maxx / RES_X_TARG
  645.                     bal(hrac(i).bal).m = 4 / 4 * pi * PLAY_BALL_R ^ 2 / 2 'Players have only half density
  646.                     bal(hrac(i).bal).col = hrac(i).col
  647.                     bal(hrac(i).bal).strat = 3
  648.                 END IF
  649.             NEXT i
  650.  
  651.             '---decide initially tagged player-------------------------------------------------------------
  652.             zacinalNaposledyBal = stav.zacinabal
  653.             stav.balbudeto = stav.zacinabal
  654.             DO
  655.                 stav.zacinabal = stav.zacinabal + 1
  656.                 IF stav.zacinabal > stav.pocetgulx THEN stav.zacinabal = 1
  657.             LOOP UNTIL bal(stav.zacinabal).riadena = 1
  658.             stav.baljeto = stav.zacinabal
  659.  
  660.             FOR b = 1 TO stav.pocethracov
  661.                 bal(hrac(b).bal).protivnik = stav.baljeto * -(hrac(b).bal <> stav.baljeto)
  662.             NEXT b
  663.             bal(stav.zacinabal).protivnik = zacinalNaposledyBal
  664.  
  665.             '----------------------------------game action loop--------------------------------------------
  666.             DO
  667.                 game.tstart = TIMER(.001)
  668.                 _LIMIT stav.fps
  669.                 CLS 1
  670.                 k = _KEYHIT
  671.                 IF k = 116 AND game.clientid > 0 AND game.msgwrite = 0 THEN game.msgwrite = 1: k = 0 'message writing mode
  672.                 IF game.msgwrite = 1 THEN WriteMessage k: k = 0
  673.                 IF NOT (game.isnetgame = 1 AND game.clienth <> 0) AND (k = 112 OR k = 102) THEN stav.pauza = 1 - stav.pauza
  674.  
  675.                 DrawScore 'Draw players score on screen
  676.                 DisplayMessages 'Draw messages
  677.  
  678.                 '---debug code begin---------------------------------------------
  679.                 IF k = 98 THEN game.printdebug = -(game.printdebug = 0)
  680.                 IF game.printdebug = 1 THEN DebugScreen k 'display debug info on screen
  681.                 '---debug code end-----------------------------------------------
  682.  
  683.                 IF k = 99 THEN game.show_stats = -(game.show_stats = 0)
  684.                 IF game.show_stats = 1 THEN DisplayStatistics 'display match statistics
  685.  
  686.                 IF stav.pauza = 0 THEN
  687.                     FOR i = 1 TO stav.pocethracov
  688.                         ResolvePlayerControl bal(hrac(i).bal), klav(hrac(i).klavesy) 'resolve players control
  689.                         IF bal(hrac(i).bal).riadena = 1 THEN
  690.                             CalculatePlayerState bal(hrac(i).bal) 'calculate player status
  691.                         END IF
  692.                     NEXT i
  693.  
  694.                     FOR n = 1 TO stav.pocetgul
  695.                         PerformMovement bal(n) 'do ball movement
  696.                     NEXT n
  697.  
  698.                     FOR i = 1 TO stav.pocetgul
  699.                         FOR j = i + 1 TO stav.pocetgul
  700.                             BounceTest bal(i), bal(j) 'Test balls bounce
  701.                         NEXT j
  702.                     NEXT i
  703.                 ELSE
  704.                     PauseGame
  705.                 END IF
  706.  
  707.                 IF game.isnetgame = 1 THEN SyncNetGameData
  708.  
  709.                 FOR n = 1 TO stav.pocetgul
  710.                     DrawBall bal(n) 'draw ball
  711.                 NEXT n
  712.  
  713.                 IF stav.vitaz <> 0 THEN DrawWinScreen
  714.  
  715.                 IF NOT (game.isnetgame = 1 AND game.clienth <> 0) AND (k = 102) THEN stav.pauza = 1
  716.                 IF k = 27 AND game.isnetgame = 1 THEN AddMessage "To quit network game press 't' and type '/q'", MSG_COL, MSG_DUR_STD, 0: k = 0
  717.                 IF k = 27 AND game.isnetgame = 0 THEN stav.koniec_kola = 2
  718.  
  719.                 _DISPLAY 'Turn video page over
  720.                 game.tend = TIMER(.001)
  721.                 CalculateFps
  722.             LOOP UNTIL stav.koniec_kola >= 1
  723.             '---------------------------end of game action loop---------------------------------------------
  724.  
  725.             IF stav.vitaz <> 0 THEN k = 0
  726.             anyHumanPlayer = HumanPlayersCount
  727.             resumegame = 0
  728.             DO 'wait for resume if not net game
  729.                 _LIMIT stav.fps
  730.                 IF k = 27 AND game.hosth = 0 THEN stav.koniec_kola = 2
  731.                 FOR i = 1 TO stav.pocethracov
  732.                     IF klav(hrac(i).klavesy).typ = 3 THEN ReadGamepad klav(hrac(i).klavesy)
  733.                     IF klav(hrac(i).klavesy).isshoota THEN resumegame = 1
  734.                     IF klav(hrac(i).klavesy).isshootb THEN stav.koniec_kola = 2
  735.                     IF klav(hrac(i).klavesy).typ = 1 THEN IF _KEYDOWN(klav(hrac(i).klavesy).shoota) THEN resumegame = 1
  736.                 NEXT i
  737.                 k = _KEYHIT
  738.             LOOP UNTIL (resumegame = 1) OR (anyHumanPlayer = 0) OR (stav.koniec_kola = 2) OR (game.isnetgame = 1)
  739.             '---------------------------end of round-----------------------------------------------------------
  740.         LOOP UNTIL stav.koniec_kola = 2 OR (stav.AIallowed = 0 AND anyHumanPlayer < 2)
  741.         IF (stav.AIallowed = 0 AND anyHumanPlayer < 2) THEN AddMessage "Match was ended for less than 2 players left", MSG_COL, MSG_DUR_STD, 0
  742.     END IF
  743.     '---------------------------end of game------------------------------------------------------------
  744.     game.autostart_time = TIMER + stav.autostart_set
  745. LOOP UNTIL game.exituj = 1
  746. CLS 1
  747. '---------------------------end of program------------------------------------------------------------
  748.  
  749. '////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  750. '----------------------------------------------------------------------------------------------------------------------------------------------------
  751. SUB DrawWinScreen
  752.     DIM statstab(1 TO PLAYER_NET_MAX) AS STRING
  753.     IF stav.vitaz > 0 THEN
  754.         IF hrac(stav.vitaz).skore >= stav.winscore THEN windelay = DELAY_MATCHEND ELSE windelay = DELAY_ROUNDSTART
  755.         IF game.autostart_time - windelay > TIMER THEN game.autostart_time = game.autostart_time - 86399
  756.         IF (game.autostart_time <= TIMER) AND (game.isnetgame = 0 OR game.hosth <> 0) THEN
  757.             stav.koniec_kola = 1 'end of round
  758.             IF hrac(stav.vitaz).skore >= stav.winscore THEN stav.koniec_kola = 2 'end of match either
  759.         END IF
  760.         IF (game.isnetgame = 0 OR game.hosth <> 0) THEN stav.autostart_timeleft = game.autostart_time - TIMER
  761.         IF INT(stav.autostart_timeleft) >= 0 THEN timeleft$ = "  (" + LTRIM$(STR$(INT(stav.autostart_timeleft))) + ")" ELSE timeleft$ = ""
  762.         IF hrac(stav.vitaz).skore < stav.winscore THEN
  763.             winstr1$ = "....and the winner of this round is... " + RTRIM$(hrac(stav.vitaz).meno) + timeleft$
  764.             winstr2$ = "Press <fire> for the next round"
  765.         ELSE
  766.             winstr1$ = "...>>> " + RTRIM$(hrac(stav.vitaz).meno) + " <<<.... scores " + LTRIM$(STR$(stav.winscore)) + " and becomes the MATCH WINNER!" + timeleft$
  767.             winstr2$ = "Press <fire> to return to the menu"
  768.             DisplayStatistics
  769.         END IF
  770.         COLOR bal(hrac(stav.vitaz).bal).col
  771.         _PRINTSTRING ((game.maxx - LEN(winstr1$) * 10) / 2, (game.maxy - 50) / 2), winstr1$
  772.         IF stav.koniec_kola > 0 THEN _PRINTSTRING ((game.maxx - LEN(winstr2$) * 10) / 2, (game.maxy - 50) / 2 + 50), winstr2$
  773.     END IF
  774.  
  775. '----------------------------------------------------------------------------------------------------------------------------------------------------
  776. SUB DisplayStatistics
  777.     IF stav.pocethracov <= PLAYER_NET_MAX THEN playercount = stav.pocethracov ELSE playercount = PLAYER_NET_MAX
  778.     row = INT((game.maxy - (playercount + 4) * _FONTHEIGHT) / _FONTHEIGHT)
  779.     col = INT((game.maxx - ((playercount + 2) * 11 * _FONTWIDTH)) / 2 / _FONTWIDTH)
  780.     IF row < 1 THEN row = 1
  781.     IF col < 1 THEN col = 1
  782.     COLOR MSG_COL
  783.     LOCATE row, col
  784.     PRINT "Match statistics - tags given by player"
  785.     LOCATE row + 1, col
  786.     PRINT STRING$((playercount + 2) * 11, "-"),
  787.     LOCATE row + 2, col
  788.     PRINT "Player"
  789.     FOR i = 1 TO playercount
  790.         COLOR hrac(i).col
  791.         LOCATE row + 2, col + i * 11
  792.         PRINT RTRIM$(hrac(i).meno)
  793.     NEXT i
  794.     COLOR MSG_COL
  795.     LOCATE row + 2, col + i * 11
  796.     PRINT "Tags given"
  797.     FOR i = 1 TO playercount
  798.         tagsum = 0
  799.         LOCATE row + 2 + i, col
  800.         COLOR hrac(i).col
  801.         PRINT RTRIM$(hrac(i).meno)
  802.         FOR j = 1 TO playercount
  803.             LOCATE row + 2 + i, col + j * 11
  804.             IF stats(i, j) > 0 THEN PRINT STR$(stats(i, j))
  805.             tagsum = tagsum + stats(i, j)
  806.         NEXT j
  807.         LOCATE row + 2 + i, col + j * 11
  808.         PRINT STR$(tagsum)
  809.     NEXT i
  810.  
  811. '----------------------------------------------------------------------------------------------------------------------------------------------------
  812. FUNCTION HumanPlayersCount 'set pre-game parameters
  813.     HumanPlayersCount = 0
  814.     FOR i = 1 TO stav.pocethracov
  815.         IF bal(hrac(i).bal).complayer = 0 THEN HumanPlayersCount = HumanPlayersCount + 1
  816.     NEXT i
  817.  
  818. '----------------------------------------------------------------------------------------------------------------------------------------------------
  819. SUB SetGlobalParameters 'set pre-game parameters
  820.     '-------------------------------set game session parameters----------------------------------------------------------------------------------------
  821.     game.fpsactual = FPS
  822.     game.isnetgame = 0
  823.     game.clienth = 0
  824.     game.clientid = 0
  825.     game.hosth = 0
  826.     game.host_port = HOSTPORT_DEFAULT
  827.     game.host_ip = HOSTIP_DEFAULT
  828.     game.printdebug = DEBUGMODE
  829.     game.exituj = 0
  830.     game.start = 0
  831.     game.newmsg = 0
  832.     IF RES_X = -1 THEN game.maxx = _DESKTOPWIDTH ELSE game.maxx = RES_X
  833.     IF RES_Y = -1 THEN game.maxy = _DESKTOPHEIGHT ELSE game.maxy = RES_Y
  834.     game.msgcount = 0
  835.     game.msgwrite = 0
  836.     game.msgtext = ""
  837.     game.show_stats = 0
  838.     sync.syncstr = GETITEM_SYNC
  839.     sync.synctime = 0
  840.  
  841.     '-------------------------------set game world parameters----------------------------------------------------------------------------------------
  842.     pi = _PI
  843.     stav.syncstr = GETITEM_STAV
  844.     stav.synctime = 0
  845.     stav.pauza = 0
  846.     stav.gameplay = 0
  847.     stav.fps = FPS
  848.     stav.pocethracov = PLAYER_COUNT_INI
  849.     stav.pocetgulzvoleny = POCET_GUL_INI
  850.     stav.pocetgul = POCET_GUL_INI
  851.     stav.energy_allmin = ENERGY_ALL_MIN
  852.     stav.energy_playermin = ENERGY_PLAYER_MIN
  853.     stav.energy_playermax = ENERGY_PLAYER_MAX
  854.     stav.energydec = ENERGY_DEC
  855.     stav.odpor = FRICTION
  856.     stav.minx = 0
  857.     stav.miny = 0
  858.     stav.maxx = RES_X_TARG
  859.     stav.maxy = RES_Y_TARG
  860.     stav.vmax = V_MAX * stav.maxx / RES_X_TARG
  861.     stav.maxcol = MAX_COL
  862.     stav.boostallowed = 1
  863.     stav.massallowed = 0 'sticky balls problem if mass allowed unsolved yet
  864.     stav.autostart_set = 0
  865.     game.autostart_time = 0
  866.     stav.AIallowed = 1
  867.     stav.winscore = 3
  868.     stav.energy_player_start = (stav.energy_allmin / stav.pocethracov) 'calculate  players energy
  869.     IF stav.energy_player_start < stav.energy_playermin THEN stav.energy_player_start = stav.energy_playermin
  870.     IF stav.energy_player_start > stav.energy_playermax THEN stav.energy_player_start = stav.energy_playermax
  871.  
  872.     stats(PLAYER_NET_MAX + 1, 1) = ASC(GETITEM_STAT, 1)
  873.     stats(PLAYER_NET_MAX + 1, 2) = ASC(GETITEM_STAT, 2)
  874.     stats(PLAYER_NET_MAX + 1, 3) = ASC(GETITEM_STAT, 3)
  875.  
  876.     '-------------------------------set game controllers---------------------------------------------------------------------------------------------
  877.     FOR i = 1 TO UBOUND(klav) 'set all the other game controllers as unused, few reserved for net players
  878.         IF i <= PLAYER_NET_MAX THEN
  879.             klav(i).typ = 5
  880.             klav(i).nazov = "NetControl" + LTRIM$(STR$(i))
  881.         ELSE
  882.             klav(i).typ = 0
  883.         END IF
  884.         klav(i).devid = 0
  885.         klav(i).syncstr = GETITEM_KLAV
  886.         klav(i).synctime = 0
  887.     NEXT i
  888.  
  889.     'set default controllers
  890.     kid = PLAYER_NET_MAX + 1 'CPU void controller
  891.     klav(kid).id = kid: klav(kid).devid = 1: klav(kid).nazov = "<CPU>": klav(kid).typ = 4
  892.  
  893.     kid = PLAYER_NET_MAX + 2 'keyboard set 1 controller
  894.     klav(kid).id = kid: klav(kid).devid = 1: klav(kid).nazov = "<Arrow keys + LShift>": klav(kid).typ = 1
  895.     klav(kid).up = 18432: klav(kid).down = 20480: klav(kid).left = 19200: klav(kid).right = 19712
  896.     klav(kid).shoota = 100304: klav(kid).shootb = 100306
  897.  
  898.     kid = PLAYER_NET_MAX + 3 'keyboard set 2 controller
  899.     klav(kid).id = kid: klav(kid).devid = 1: klav(kid).nazov = "<W,A,S,D + RShift>": klav(kid).typ = 1
  900.     klav(kid).up = 119: klav(kid).down = 115: klav(kid).left = 97: klav(kid).right = 100
  901.     klav(kid).shoota = 100303: klav(kid).shootb = 100305
  902.  
  903.     kid = PLAYER_NET_MAX + 4 'keyboard set 3 controller
  904.     klav(kid).id = kid: klav(kid).devid = 1: klav(kid).nazov = "<I,J,K,L + Enter>": klav(kid).typ = 1
  905.     klav(kid).up = 105: klav(kid).down = 107: klav(kid).left = 106: klav(kid).right = 108
  906.     klav(kid).shoota = 13: klav(kid).shootb = 21248
  907.  
  908.     '-------------------------------set players------------------------------------------------------------------------------------------------------
  909.     FOR i = 1 TO UBOUND(hrac) 'initial setting for all players
  910.         hrac(i).bal = i
  911.         hrac(i).meno = "Player" + LTRIM$(STR$(i))
  912.         hrac(i).skore = 0
  913.         hrac(i).klavesy = CPU_CONTROLID
  914.         hrac(i).col = 32 + i * 3
  915.         hrac(i).clientid = 0
  916.         bal(hrac(i).bal).complayer = 1
  917.         hrac(i).syncstr = GETITEM_HRAC
  918.         hrac(i).synctime = 0
  919.     NEXT i
  920.  
  921.     'personalize some players initially
  922.     bal(hrac(1).bal).complayer = 0
  923.     hrac(1).klavesy = PLAYER_NET_MAX + 2
  924.     hrac(1).meno = "Chuck"
  925.     hrac(2).meno = "Arnold"
  926.     hrac(3).meno = "Jean"
  927.     hrac(4).meno = "Bruce"
  928.     hrac(5).meno = "Dolph"
  929.     hrac(6).meno = "Sylvester"
  930.     hrac(7).meno = "Jet"
  931.     hrac(8).meno = "Dwayne"
  932.  
  933.     '-------------------------------set behaviour strategies------------------------------------------------------------------------------------------
  934.     'some void strategy
  935.     strat(1).okraj = PRIESTOR_MIN:
  936.     strat(1).okraj_rot = 0.1
  937.     strat(1).chybasmeru = 0.00:
  938.     strat(1).klucka_avgtm = 1 * FPS: strat(1).klucka_pocet = 5
  939.     strat(1).klucka_trvanie = 0.25 * FPS: strat(1).klucka_uhol = pi / 2: strat(1).klucka_uholodch = 0.2
  940.     strat(1).detekcia_sin = SIN(pi / 4): strat(1).detekcia_vzdfull = 500: strat(1).detekcia_vzdmin = 1000: strat(1).detekcia_rot = 0.5
  941.     strat(1).boost_dist = 200
  942.     strat(1).boost_chasesin = SIN(pi / 8)
  943.     strat(1).mate_cosenmax = -0.8 'cos(140/180*pi)
  944.     strat(1).mate_rot = 0.1 ' 3*pi /FPS
  945.     strat(1).mate_en_distmin = 5 * PLAY_BALL_R
  946.     strat(1).mate_distmin = 4 * PLAY_BALL_R
  947.  
  948.     'non dodging strategy
  949.     strat(2).okraj = PRIESTOR_MIN + 10
  950.     strat(2).okraj_rot = 0.1
  951.     strat(2).chybasmeru = 0.00
  952.     strat(2).reactdrop_per = 0 * FPS
  953.     strat(2).reactdrop_dur = 0 * FPS
  954.     strat(2).klucka_avgtm = 10 * FPS
  955.     strat(2).klucka_pocet = 0
  956.     strat(2).klucka_trvanie = 0 * FPS
  957.     strat(2).klucka_uhol = 0 * pi / 3
  958.     strat(2).klucka_uholodch = 0.2
  959.     strat(2).detekcia_sin = SIN(pi / 6)
  960.     strat(2).detekcia_vzdfull = 500
  961.     strat(2).detekcia_vzdmin = 1000
  962.     strat(2).detekcia_rot = 0.5
  963.     strat(2).boost_dist = 200
  964.     strat(2).boost_chasesin = SIN(pi / 8)
  965.     strat(2).mate_cosenmax = -0.8 'cos(140/180*pi)
  966.     strat(2).mate_rot = 0.1 ' 3*pi /FPS
  967.     strat(2).mate_en_distmin = 5 * PLAY_BALL_R
  968.     strat(2).mate_distmin = 4 * PLAY_BALL_R
  969.  
  970.     'dodging strategy - currently used for all AI players
  971.     strat(3).okraj = PRIESTOR_MIN + 10
  972.     strat(3).okraj_rot = 0.1
  973.     strat(3).chybasmeru = 0.30
  974.     strat(3).reactdrop_per = 0.8 * FPS
  975.     strat(3).reactdrop_dur = 0.4 * FPS
  976.     strat(3).klucka_avgtm = 5 * FPS
  977.     strat(3).klucka_pocet = 3
  978.     strat(3).klucka_trvanie = 2 * FPS
  979.     strat(3).klucka_uhol = pi * 2 / 3
  980.     strat(3).klucka_uholodch = 0.2
  981.     strat(3).detekcia_sin = SIN(pi / 6)
  982.     strat(3).detekcia_vzdfull = 300
  983.     strat(3).detekcia_vzdmin = 1500
  984.     strat(3).detekcia_rot = 0.5 '10 * pi / FPS
  985.     strat(3).boost_dist = 200
  986.     strat(3).boost_chasesin = SIN(pi / 8)
  987.     strat(3).mate_cosenmax = -0.8 'cos(140/180*pi)
  988.     strat(3).mate_rot = 0.1 ' 3*pi /FPS
  989.     strat(3).mate_en_distmin = 8 * PLAY_BALL_R
  990.     strat(3).mate_distmin = 4 * PLAY_BALL_R
  991.     strat(3).adrenalin_rate = 0.90
  992.     strat(3).adrenalin_energy = 0.30
  993.  
  994.     '-------------------------------set main menu-----------------------------------------------------------------------------------------------------
  995.     menu.item_active = 1
  996.     menu.items_count = 22
  997.     menu.cursor_col = 15
  998.     menu.cursor_width = 400
  999.     menu.item_height = FONT_H + 4
  1000.     menu.cursor_xpos = INT((game.maxx - menu.cursor_width) / 2)
  1001.     menu.ypos = INT((game.maxy - menu.items_count * menu.item_height) / 2)
  1002.     menu.cursor_out = 5
  1003.     '-------------------------------set balls---------------------------------------------------------------------------------------------------------
  1004.     FOR n = 1 TO stav.pocethracov: bal(n).syncstr = GETITEM_PBAL: bal(n).synctime = 0: NEXT n
  1005.     FOR n = stav.pocethracov + 1 TO UBOUND(bal): bal(n).syncstr = GETITEM_BAL: bal(n).synctime = 0: NEXT n
  1006.  
  1007. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1008. SUB WriteMessage (k AS INTEGER)
  1009.     DIM tmptext AS STRING
  1010.     IF game.clientid > 0 THEN
  1011.         msginput_x = 10
  1012.         msginput_y = RES_Y - 20
  1013.  
  1014.         tmptext = LEFT$(game.msgtext, LEN(RTRIM$(game.msgtext)) - 1)
  1015.         IF k >= 32 AND k <= 126 AND LEN(tmptext) < MSG_LEN_MAX THEN tmptext = tmptext + CHR$(k)
  1016.         IF k = 8 AND LEN(tmptext) > 0 THEN tmptext = LEFT$(tmptext, LEN(tmptext) - 1)
  1017.         IF k = 27 THEN game.msgwrite = 0
  1018.  
  1019.         COLOR MSG_COL
  1020.         _PRINTSTRING (msginput_x, msginput_y), "Type message>>  " + tmptext
  1021.         IF k = -1 AND game.msgwrite = 0 THEN _PRINTSTRING (msginput_x, msginput_y), "Press 't' to writte text message to other players" + tmptext
  1022.  
  1023.  
  1024.         IF k = 13 THEN
  1025.             IF LEFT$(tmptext, 1) = "/" THEN
  1026.                 AddMessage tmptext, MSG_COL, MSG_DUR_STD, 0
  1027.             ELSE
  1028.                 AddMessage RTRIM$(hrac(game.clientid).meno) + "> " + tmptext, hrac(game.clientid).col, MSG_DUR_STD, 1
  1029.             END IF
  1030.             game.msgtext = CHR$(92)
  1031.             game.msgwrite = 0
  1032.         ELSE
  1033.             game.msgtext = tmptext + CHR$(92)
  1034.         END IF
  1035.     END IF
  1036.  
  1037. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1038. SUB SetUpControllers 'detect gamepads and set them as game controllers
  1039.     DIM klav_volne AS INTEGER, devices AS INTEGER
  1040.     DIM dev AS STRING
  1041.  
  1042.     FOR i = 1 TO UBOUND(klav) 'dispose all gamepad constrollers
  1043.         IF klav(i).typ = 3 THEN
  1044.             klav(i).typ = 0
  1045.             klav(i).devid = 0
  1046.         END IF
  1047.     NEXT i
  1048.     klav_volne = 1
  1049.     game.pocetgamepadov = 0
  1050.     DO
  1051.         klav_volne = klav_volne + 1
  1052.     LOOP UNTIL klav(klav_volne).typ = 0 OR klav_volne = UBOUND(klav)
  1053.     klav_used = klav_volne - 1
  1054.     devices = _DEVICES 'must be read prior to other device functions usage
  1055.     FOR i = 1 TO devices
  1056.         dev = _DEVICE$(i)
  1057.         IF INSTR(dev, "[CONTROLLER]") > 0 AND INSTR(dev, "[AXIS]") > 0 AND klav_volne <= UBOUND(klav) THEN 'set new controller if gamepad
  1058.             klav(klav_volne).id = klav_volne
  1059.             klav(klav_volne).devid = i
  1060.             klav(klav_volne).typ = 3
  1061.             klav(klav_volne).nazov = "<Gamepad" + LTRIM$(STR$(klav_volne - klav_used)) + ">"
  1062.             klav(klav_volne).xaxis = 1
  1063.             klav(klav_volne).yaxis = 2
  1064.             klav(klav_volne).dxaxis = _LASTAXIS(i) - 1
  1065.             klav(klav_volne).dyaxis = _LASTAXIS(i)
  1066.             klav(klav_volne).shoota = 1
  1067.             klav(klav_volne).shootb = 8
  1068.             klav_volne = klav_volne + 1
  1069.             game.pocetgamepadov = game.pocetgamepadov + 1
  1070.         END IF
  1071.     NEXT i
  1072.     game.pocetkontrolerov = klav_volne - 1
  1073.  
  1074. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1075. SUB MainMenuScreen 'perform choises in main menu
  1076.  
  1077. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1078. SUB SyncNetGameData
  1079.     DIM i AS INTEGER, isync AS tsyncvar, clklav AS tklavesy, clhrac AS thrac, imsg AS tmessage
  1080.     DIM sync_stav AS INTEGER, sync_pbal AS INTEGER, sync_bal AS INTEGER, sync_hrac AS INTEGER
  1081.     IF game.isnetgame = 1 THEN
  1082.         '--------------------------host side---------------------------------------------------------------------------------------------------------
  1083.         IF game.hosth <> 0 THEN 'sync host data
  1084.             FOR i = PLAYER_NET_MIN TO PLAYER_NET_MAX 'for each client try to connect
  1085.                 IF client(i).conn = 0 AND i <= stav.pocethracov THEN
  1086.                     client(i).conn = _OPENCONNECTION(game.hosth) 'try to open connection if not open
  1087.                     client(i).get_item = GETITEM_SYNC
  1088.                     client(i).get_isdesync = 0
  1089.                     client(i).get_time = TIMER(.001)
  1090.                     IF client(i).conn <> 0 THEN 'if connection opened
  1091.                         client(i).ip = _CONNECTIONADDRESS(client(i).conn)
  1092.                         PLAY "MBL64O2CEG>C"
  1093.                         IF stav.gameplay = 1 THEN
  1094.                             client(i).spectating = 1
  1095.                             game.sync_stats = 1
  1096.                             msgtext$ = " joins the game from the next round"
  1097.                         ELSE
  1098.                             client(i).spectating = 0
  1099.                             msgtext$ = " has joined the game"
  1100.                         END IF
  1101.                         AddMessage "Player " + RTRIM$(hrac(i).meno) + msgtext$, MSG_COL, MSG_DUR_STD, 1
  1102.                     END IF
  1103.                 END IF
  1104.                 IF (TIMER(.001) - client(i).get_time) > NET_TIMEOUT_LIMIT AND client(i).conn <> 0 THEN CloseClientConn (i)
  1105.             NEXT i
  1106.             FOR i = PLAYER_NET_MIN TO PLAYER_NET_MAX 'sync data with each client
  1107.                 sync.clientid = i
  1108.                 IF client(i).conn <> 0 THEN
  1109.                     DO
  1110.                         IF client(i).get_isdesync = 1 THEN 'flush acquired data if desynced
  1111.                             GET #client(i).conn, , flush$
  1112.                             client(i).get_isdesync = 0
  1113.                             client(i).get_item = GETITEM_SYNC
  1114.                         END IF
  1115.  
  1116.                         IF client(i).get_item = GETITEM_SYNC THEN 'first get sync info on what item to get next
  1117.                             GET #client(i).conn, , isync
  1118.                             IF NOT EOF(client(i).conn) THEN
  1119.                                 IF isync.syncstr = GETITEM_SYNC THEN
  1120.                                     client(i).get_item = isync.itemstr
  1121.                                     client(i).get_time = TIMER(.001)
  1122.                                     client(i).isdesynced = isync.isdesync
  1123.                                 ELSE
  1124.                                     client(i).get_isdesync = 1
  1125.                                 END IF
  1126.                             END IF
  1127.                         END IF
  1128.  
  1129.                         IF client(i).get_item = GETITEM_KLAV THEN 'get controller state from client
  1130.                             GET #client(i).conn, , clklav
  1131.                             IF NOT EOF(client(i).conn) THEN
  1132.                                 IF clklav.syncstr = GETITEM_KLAV THEN
  1133.                                     klav(hrac(i).klavesy).isup = clklav.isup
  1134.                                     klav(hrac(i).klavesy).isdown = clklav.isdown
  1135.                                     klav(hrac(i).klavesy).isright = clklav.isright
  1136.                                     klav(hrac(i).klavesy).isleft = clklav.isleft
  1137.                                     klav(hrac(i).klavesy).isshoota = clklav.isshoota
  1138.                                     klav(hrac(i).klavesy).isshootb = clklav.isshootb
  1139.                                     klav(hrac(i).klavesy).gvektor = clklav.gvektor
  1140.                                     client(i).get_time = TIMER(.001)
  1141.                                 ELSE
  1142.                                     client(i).get_isdesync = 1
  1143.                                 END IF
  1144.                                 client(i).get_item = GETITEM_SYNC
  1145.                             END IF
  1146.                         END IF
  1147.  
  1148.                         IF client(i).get_item = GETITEM_HRAC THEN 'get client's player
  1149.                             GET #client(i).conn, , clhrac
  1150.                             IF NOT EOF(client(i).conn) THEN
  1151.                                 IF clhrac.syncstr = GETITEM_HRAC THEN
  1152.                                     clhrac.skore = hrac(i).skore 'keep players socre
  1153.                                     hrac(i) = clhrac 'get player data from client
  1154.                                     hrac(i).klavesy = i 'set player net controlled
  1155.                                     hrac(i).clientid = i 'set player's clientid
  1156.                                     client(i).get_time = TIMER(.001)
  1157.                                 ELSE
  1158.                                     client(i).get_isdesync = 1
  1159.                                 END IF
  1160.                                 client(i).get_item = GETITEM_SYNC
  1161.                             END IF
  1162.                         END IF
  1163.  
  1164.                         IF client(i).get_item = GETITEM_MESG THEN 'get message from client
  1165.                             GET #client(i).conn, , imsg
  1166.                             IF NOT EOF(client(i).conn) THEN
  1167.                                 IF imsg.syncstr = GETITEM_MESG THEN
  1168.                                     AddMessage imsg.text, imsg.col, imsg.time_dur, 1
  1169.                                     msg(game.msgcount).clientid = imsg.clientid
  1170.                                     client(i).get_time = TIMER(.001)
  1171.                                 ELSE
  1172.                                     client(i).get_isdesync = 1
  1173.                                 END IF
  1174.                                 client(i).get_item = GETITEM_SYNC
  1175.                             END IF
  1176.                         END IF
  1177.  
  1178.                     LOOP UNTIL EOF(client(i).conn)
  1179.                 END IF
  1180.             NEXT i
  1181.  
  1182.             IF TIMER(.001) - stav.synctime > UPDATE_PER_STAV THEN 'check if update time
  1183.                 sync_stav = 1
  1184.             ELSE sync_stav = 0
  1185.             END IF
  1186.             IF TIMER(.001) - bal(1).synctime > UPDATE_PER_PLBAL THEN
  1187.                 FOR j = 1 TO stav.pocethracov: ipbal(j) = bal(j): NEXT j 'fill transfer array
  1188.                 sync_pbal = 1
  1189.             ELSE sync_pbal = 0
  1190.             END IF
  1191.             IF TIMER(.001) - bal(stav.pocethracov + 1).synctime > UPDATE_PER_BAL THEN
  1192.                 FOR j = 1 TO stav.pocetgulzvoleny: ibal(j) = bal(j + stav.pocethracov): NEXT j 'fill transfer array
  1193.                 sync_bal = 1
  1194.             ELSE sync_bal = 0
  1195.             END IF
  1196.             IF TIMER(.001) - hrac(1).synctime > UPDATE_PER_HRAC THEN
  1197.                 sync_hrac = 1
  1198.             ELSE sync_hrac = 0
  1199.             END IF
  1200.  
  1201.             FOR i = PLAYER_NET_MIN TO PLAYER_NET_MAX 'send host data to each client
  1202.                 IF client(i).conn <> 0 THEN
  1203.                     sync.clientid = i
  1204.  
  1205.                     IF client(i).isdesynced = 1 THEN 'send sync var if client desynced
  1206.                         sync.synctime = TIMER(.001)
  1207.                         sync.itemstr = GETITEM_SYNC
  1208.                         PUT #client(i).conn, , sync
  1209.                         'PRINT "Resyncing client "; i 'TEMP DEBUG line
  1210.                     END IF
  1211.  
  1212.                     IF sync_stav = 1 AND client(i).isdesynced = 0 THEN 'send world state
  1213.                         sync.synctime = TIMER(.001)
  1214.                         sync.itemstr = GETITEM_STAV
  1215.                         PUT #client(i).conn, , sync
  1216.                         stav.synctime = TIMER(.001)
  1217.                         PUT #client(i).conn, , stav
  1218.                     END IF
  1219.  
  1220.                     IF sync_pbal = 1 AND client(i).isdesynced = 0 THEN 'send player balls
  1221.                         sync.synctime = TIMER(.001):
  1222.                         sync.itemstr = GETITEM_PBAL
  1223.                         PUT #client(i).conn, , sync
  1224.                         bal(1).synctime = TIMER(.001)
  1225.                         PUT #client(i).conn, , ipbal()
  1226.                     END IF
  1227.  
  1228.                     IF sync_bal = 1 AND client(i).isdesynced = 0 THEN 'send nonplayer balls
  1229.                         sync.synctime = TIMER(.001)
  1230.                         sync.itemstr = GETITEM_BAL
  1231.                         bal(stav.pocethracov + 1).synctime = TIMER(.001)
  1232.                         PUT #client(i).conn, , sync
  1233.                         PUT #client(i).conn, , ibal()
  1234.                     END IF
  1235.  
  1236.                     IF sync_hrac = 1 AND client(i).isdesynced = 0 THEN 'IF stav.gameplay = 0 OR stav.vitaz <> 0 THEN 'send players
  1237.                         FOR j = 1 TO PLAYER_NET_MAX: ihrac(j) = hrac(j): NEXT j 'fill transfer array
  1238.                         sync.itemstr = GETITEM_HRAC
  1239.                         sync.synctime = TIMER(.001)
  1240.                         PUT #client(i).conn, , sync
  1241.                         hrac(1).synctime = TIMER(.001)
  1242.                         PUT #client(i).conn, , ihrac()
  1243.                     END IF
  1244.  
  1245.                     IF game.sync_stats = 1 AND client(i).isdesynced = 0 THEN 'send match statistics
  1246.                         sync.itemstr = GETITEM_STAT
  1247.                         sync.synctime = TIMER(.001)
  1248.                         PUT #client(i).conn, , sync
  1249.                         PUT #client(i).conn, , stats()
  1250.                     END IF
  1251.  
  1252.                     IF game.newmsg = 1 AND client(i).isdesynced = 0 THEN 'send new messages
  1253.                         FOR j = 1 TO game.msgcount
  1254.                             IF msg(j).distribute = 1 AND msg(j).clientid <> i THEN
  1255.                                 sync.itemstr = GETITEM_MESG:
  1256.                                 sync.synctime = TIMER(.001):
  1257.                                 PUT #client(i).conn, , sync
  1258.                                 msg(j).synctime = TIMER(.001):
  1259.                                 PUT #client(i).conn, , msg(j)
  1260.                             END IF
  1261.                         NEXT j
  1262.                     END IF
  1263.  
  1264.                 END IF
  1265.             NEXT i
  1266.             game.sync_stats = 0 'clear send statistics flag
  1267.             game.newmsg = 0 'clear messages distributed flags
  1268.             FOR j = 1 TO game.msgcount
  1269.                 msg(j).distribute = 0
  1270.             NEXT j
  1271.         END IF
  1272.  
  1273.         '-------------------client side-------------------------------------------------------------------------------
  1274.         IF game.clienth <> 0 THEN 'client getting data from host
  1275.             DO
  1276.                 IF game.get_isdesync = 1 THEN 'flush acquired data if desynced
  1277.                     'PRINT "Resyncing... "; game.get_item 'TEMP debug line
  1278.                     DO
  1279.                         GET #game.clienth, , isync
  1280.                     LOOP UNTIL (NOT EOF(game.clienth) AND isync.syncstr = GETITEM_SYNC) OR (TIMER(.001) - game.get_time) > NET_TIMEOUT_SYNC
  1281.  
  1282.                     'GET #game.clienth, , flush$
  1283.  
  1284.                     IF NOT EOF(game.clienth) AND isync.syncstr = GETITEM_SYNC THEN
  1285.                         'PRINT "==================RESYNCED================ "; game.get_item; "-"; isync.itemstr 'TEMP debug line
  1286.                         game.get_item = isync.itemstr
  1287.                         game.get_isdesync = 0
  1288.                     ELSE
  1289.                         'PRINT "Desynced!!!!!!!!!!!!! "; game.get_item 'TEMP debug line
  1290.                     END IF
  1291.                 END IF
  1292.  
  1293.                 IF game.get_item = GETITEM_SYNC THEN 'first get sync info on what item to get next
  1294.                     GET #game.clienth, , isync
  1295.                     IF NOT EOF(game.clienth) THEN
  1296.                         IF isync.syncstr = GETITEM_SYNC THEN
  1297.                             game.clientid = isync.clientid
  1298.                             game.get_item = isync.itemstr
  1299.                             game.get_time = TIMER(.001)
  1300.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1301.                         ELSE
  1302.                             game.get_isdesync = 1
  1303.                         END IF
  1304.                     END IF
  1305.                 END IF
  1306.  
  1307.                 IF game.get_item = GETITEM_STAV THEN 'get world state
  1308.                     GET #game.clienth, , istav
  1309.                     IF NOT EOF(game.clienth) THEN
  1310.                         IF istav.syncstr = GETITEM_STAV THEN
  1311.                             IF istav.synctime > stav.synctime THEN
  1312.                                 stav = istav
  1313.                             END IF
  1314.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1315.                         ELSE
  1316.                             game.get_isdesync = 1
  1317.                         END IF
  1318.                         game.get_item = GETITEM_SYNC
  1319.                     END IF
  1320.                 END IF
  1321.  
  1322.                 IF game.get_item = GETITEM_PBAL THEN 'get player balls data
  1323.                     GET #game.clienth, , ipbal()
  1324.                     IF NOT EOF(game.clienth) THEN
  1325.                         IF (ipbal(1).syncstr = GETITEM_PBAL) THEN
  1326.                             IF (ipbal(1).synctime > bal(1).synctime) THEN 'AND (TIMER(.001) - game.timer_pballs) < (ipbal(1).synctime - bal(1).synctime + 0.05) THEN
  1327.                                 FOR i = 1 TO stav.pocethracov
  1328.                                     bal(i) = ipbal(i)
  1329.                                 NEXT i
  1330.                             END IF
  1331.                             game.timer_pballs = TIMER(.001)
  1332.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1333.                         ELSE
  1334.                             game.get_isdesync = 1
  1335.                         END IF
  1336.                         game.get_item = GETITEM_SYNC
  1337.                     END IF
  1338.                 END IF
  1339.  
  1340.                 IF game.get_item = GETITEM_BAL THEN 'get nonplayer balls data
  1341.                     GET #game.clienth, , ibal()
  1342.                     IF NOT EOF(game.clienth) THEN
  1343.                         IF (ibal(1).syncstr = GETITEM_BAL) THEN
  1344.                             IF (ibal(1).synctime > bal(stav.pocethracov + 1).synctime) THEN
  1345.                                 FOR i = 1 TO stav.pocetgulzvoleny
  1346.                                     bal(stav.pocethracov + i) = ibal(i)
  1347.                                 NEXT i
  1348.                             END IF
  1349.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1350.                         ELSE
  1351.                             game.get_isdesync = 1
  1352.                         END IF
  1353.                         game.get_item = GETITEM_SYNC
  1354.                     END IF
  1355.                 END IF
  1356.  
  1357.                 IF game.get_item = GETITEM_MESG THEN 'get message
  1358.                     GET #game.clienth, , imsg
  1359.                     IF NOT EOF(game.clienth) THEN
  1360.                         IF (imsg.syncstr = GETITEM_MESG) THEN
  1361.                             AddMessage imsg.text, imsg.col, imsg.time_dur, 0
  1362.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1363.                         ELSE
  1364.                             game.get_isdesync = 1
  1365.                         END IF
  1366.                         game.get_item = GETITEM_SYNC
  1367.                     END IF
  1368.                 END IF
  1369.  
  1370.                 IF game.get_item = GETITEM_HRAC THEN 'get players data
  1371.                     GET #game.clienth, , ihrac()
  1372.                     IF NOT EOF(game.clienth) THEN
  1373.                         IF ihrac(1).syncstr = GETITEM_HRAC THEN
  1374.                             IF ihrac(1).synctime > hrac(1).synctime THEN
  1375.                                 FOR i = 1 TO PLAYER_NET_MAX
  1376.                                     IF i <> game.clientid AND game.clientid > 0 THEN 'if another client's player
  1377.                                         hrac(i) = ihrac(i) 'get player data from host
  1378.                                         hrac(i).klavesy = i 'set player net controlled
  1379.                                     ELSE 'if my client player
  1380.                                         hrac(i).skore = ihrac(i).skore
  1381.                                         hrac(i).bal = ihrac(i).bal
  1382.                                         hrac(i).clientid = ihrac(i).clientid
  1383.                                     END IF
  1384.                                 NEXT i
  1385.                             END IF
  1386.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1387.                         ELSE
  1388.                             game.get_isdesync = 1
  1389.                         END IF
  1390.                         game.get_item = GETITEM_SYNC
  1391.                     END IF
  1392.                 END IF
  1393.  
  1394.                 IF game.get_item = GETITEM_STAT THEN 'get match statistics
  1395.                     GET #game.clienth, , istats()
  1396.                     IF NOT EOF(game.clienth) THEN
  1397.                         IF (istats(PLAYER_NET_MAX + 1, 1) + istats(PLAYER_NET_MAX + 1, 2) + istats(PLAYER_NET_MAX + 1, 3)) = (ASC(GETITEM_STAT, 1) + ASC(GETITEM_STAT, 2) + ASC(GETITEM_STAT, 3)) THEN
  1398.                             FOR i = 1 TO UBOUND(stats, 1)
  1399.                                 FOR j = 1 TO UBOUND(stats, 2)
  1400.                                     stats(i, j) = istats(i, j)
  1401.                                 NEXT j
  1402.                             NEXT i
  1403.                             game.sync_stats = 0
  1404.                             'PRINT "Synced"; isync.itemstr 'TEMP debug line
  1405.                         ELSE
  1406.                             game.get_isdesync = 1
  1407.                         END IF
  1408.                         game.get_item = GETITEM_SYNC
  1409.                     END IF
  1410.                 END IF
  1411.  
  1412.             LOOP UNTIL EOF(game.clienth)
  1413.  
  1414.             IF game.clientid > 0 THEN 'send clients data to host
  1415.                 sync.synctime = TIMER(.001)
  1416.  
  1417.                 'IF stav.gameplay = 1 THEN 'send client controller to host
  1418.                 sync.itemstr = GETITEM_KLAV
  1419.                 sync.synctime = TIMER(.001)
  1420.                 sync.isdesync = game.get_isdesync
  1421.                 PUT #game.clienth, , sync
  1422.                 PUT #game.clienth, , klav(hrac(game.clientid).klavesy)
  1423.                 'END IF
  1424.  
  1425.                 'IF stav.gameplay = 0 THEN 'send client player to host
  1426.                 sync.itemstr = GETITEM_HRAC
  1427.                 sync.synctime = TIMER(.001)
  1428.                 PUT #game.clienth, , sync
  1429.                 PUT #game.clienth, , hrac(game.clientid)
  1430.                 'END IF
  1431.  
  1432.                 IF game.newmsg = 1 AND game.msgcount > 0 THEN 'send client player to host
  1433.                     IF msg(game.msgcount).distribute = 1 THEN
  1434.                         sync.itemstr = GETITEM_MESG
  1435.                         sync.synctime = TIMER(.001)
  1436.                         PUT #game.clienth, , sync
  1437.                         PUT #game.clienth, , msg(game.msgcount)
  1438.                         msg(game.msgcount).distribute = 0
  1439.                     END IF
  1440.                     game.newmsg = 0
  1441.                 END IF
  1442.             END IF
  1443.  
  1444.             IF (TIMER(.001) - game.get_time) > NET_TIMEOUT_LIMIT THEN ResetNetGame
  1445.         END IF
  1446.     END IF
  1447.  
  1448. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1449. SUB CloseClientConn (clidx AS INTEGER)
  1450.     IF client(clidx).conn <> 0 THEN
  1451.         CLOSE client(clidx).conn
  1452.         client(clidx).conn = 0
  1453.     END IF
  1454.     hrac(clidx).clientid = 0
  1455.     hrac(clidx).klavesy = CPU_CONTROLID
  1456.     hrac(clidx).isready = 0
  1457.     bal(hrac(clidx).bal).complayer = 1
  1458.     IF stav.AIallowed = 0 AND klav(hrac(clidx).klavesy).typ = 4 THEN
  1459.         bal(hrac(clidx).bal).energia = 0
  1460.         CalculatePlayerState bal(hrac(clidx).bal)
  1461.     END IF
  1462.     AddMessage "Player " + RTRIM$(hrac(clidx).meno) + " was disconnected", MSG_COL, MSG_DUR_STD, 1
  1463.  
  1464. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1465. SUB ResetNetGame
  1466.     IF game.clienth <> 0 THEN 'try to close client connection
  1467.         CLOSE game.clienth
  1468.         game.clienth = 0
  1469.         game.clientid = 0
  1470.         stav.autostart_set = 0
  1471.         AddMessage "You have been disconnected from the host", MSG_COL, MSG_DUR_STD, 0
  1472.     END IF
  1473.     IF game.hosth <> 0 THEN 'try to close host service
  1474.         CLOSE game.hosth
  1475.         game.hosth = 0
  1476.         game.clientid = 0
  1477.         hrac(1).clientid = 0
  1478.         AddMessage "Game hosting ended", MSG_COL, MSG_DUR_STD, 0
  1479.     END IF
  1480.     game.isnetgame = 0
  1481.     FOR i = 1 TO PLAYER_COUNT_MAX
  1482.         hrac(i).clientid = 0
  1483.         IF (klav(hrac(i).klavesy).typ = 5) OR (i > PLAYER_NET_MAX AND klav(hrac(i).klavesy).typ <> 4) THEN
  1484.             hrac(i).klavesy = CPU_CONTROLID
  1485.             bal(hrac(i).bal).complayer = 1
  1486.         END IF
  1487.     NEXT i
  1488.  
  1489. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1490. SUB HostGame
  1491.     IF game.clienth <> 0 THEN ResetNetGame
  1492.     IF game.hosth = 0 THEN
  1493.         game.hosth = _OPENHOST("TCP/IP:" + STR$(game.host_port))
  1494.         IF game.hosth <> 0 THEN
  1495.             game.isnetgame = 1
  1496.             game.clientid = 1
  1497.             hrac(1).clientid = 1
  1498.             AddMessage "Game hosting started", MSG_COL, MSG_DUR_STD, 0
  1499.         ELSE
  1500.             game.isnetgame = 0
  1501.         END IF
  1502.     ELSE
  1503.         ResetNetGame
  1504.     END IF
  1505.  
  1506. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1507. SUB JoinGame
  1508.     IF game.hosth <> 0 THEN ResetNetGame
  1509.     IF game.clienth = 0 THEN
  1510.         AddMessage "Connecting to" + RTRIM$(STR$(game.host_port)) + ":" + RTRIM$(game.host_ip) + "...", MSG_COL, MSG_DUR_STD, 0
  1511.         DisplayMessages
  1512.         _DISPLAY
  1513.         game.clienth = _OPENCLIENT("TCP/IP:" + STR$(game.host_port) + ":" + RTRIM$(game.host_ip))
  1514.         IF game.clienth <> 0 THEN
  1515.             game.isnetgame = 1
  1516.             game.get_item = GETITEM_SYNC
  1517.             game.get_isdesync = 0
  1518.             game.get_time = TIMER(.001)
  1519.             game.timer_pballs = TIMER(.001)
  1520.             AddMessage "Connection opened, joining the game...", MSG_COL, MSG_DUR_STD, 0
  1521.         ELSE
  1522.             game.isnetgame = 0
  1523.         END IF
  1524.     ELSE
  1525.         CLOSE game.clienth
  1526.         ResetNetGame
  1527.     END IF
  1528.  
  1529. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1530. FUNCTION InputText$ (meno AS STRING, xpos AS INTEGER, ypos AS INTEGER, col AS INTEGER, inputwidth AS _BYTE, hint AS STRING, initval AS _BYTE, digitonly AS _BYTE) 'get text input from keyboard input
  1531.     clrstr$ = ""
  1532.     tmpstr$ = ""
  1533.     IF initval = 1 THEN res$ = RTRIM$(meno) ELSE res$ = ""
  1534.     FOR i = 1 TO inputwidth: clrstr$ = clrstr$ + " ": NEXT i
  1535.     COLOR col
  1536.     DO
  1537.         DO
  1538.             _LIMIT FPS
  1539.             k = _KEYHIT
  1540.         LOOP UNTIL k <> 0
  1541.         IF ((digitonly = 0 AND k >= 33 AND k <= 126) OR (digitonly = 1 AND k >= 48 AND k <= 57)) AND LEN(res$) < LEN(meno) THEN res$ = res$ + CHR$(k)
  1542.         IF k = 8 AND LEN(res$) > 0 THEN res$ = LEFT$(res$, LEN(res$) - 1)
  1543.         IF res$ = "" THEN tmpstr$ = hint ELSE tmpstr$ = res$
  1544.         _PRINTSTRING (xpos, ypos), clrstr$
  1545.         _PRINTSTRING (xpos, ypos), tmpstr$
  1546.         _DISPLAY
  1547.     LOOP UNTIL k = 13 OR k = 27
  1548.     IF k = 13 THEN InputText$ = res$
  1549.     IF k = 27 THEN InputText$ = CHR$(27)
  1550.  
  1551. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1552. SUB PrintMenuItem (id AS INTEGER, amenu AS tmenu, menucode AS INTEGER, playercode AS INTEGER, rollable AS INTEGER, s AS STRING, fixedwidth AS INTEGER, deltax AS INTEGER, lineoff AS INTEGER, col AS INTEGER) 'print horizontaly centered text at line y
  1553.     slen = LEN(RTRIM$(s)) * FONT_W
  1554.     IF fixedwidth > 0 THEN slen = fixedwidth
  1555.     px = INT((game.maxx - slen) / 2) + deltax
  1556.     py = amenu.ypos + (id - 1) * amenu.item_height
  1557.     COLOR col
  1558.     _PRINTSTRING (px, py), s
  1559.     IF id = amenu.item_active THEN
  1560.         IF rollable = 1 THEN
  1561.             COLOR amenu.cursor_col
  1562.             _PRINTSTRING (amenu.cursor_xpos - 10 - FONT_W, py), "<"
  1563.             _PRINTSTRING (amenu.cursor_xpos + amenu.cursor_width + 10, py), ">"
  1564.         END IF
  1565.         amenu.menucode = menucode
  1566.         amenu.playercode = playercode
  1567.     END IF
  1568. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1569. SUB PrintMenuCursor (amenu AS tmenu) 'print menu cursor
  1570.     py = amenu.ypos + (amenu.item_active - 1) * amenu.item_height
  1571.     COLOR amenu.cursor_col
  1572.     LINE (amenu.cursor_xpos - amenu.cursor_out, game.maxy - (py - amenu.cursor_out))-(amenu.cursor_xpos + amenu.cursor_out + amenu.cursor_width, game.maxy - (py + FONT_H) - amenu.cursor_out), amenu.cursor_col, B
  1573.  
  1574. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1575. SUB PauseGame 'pause gameplay
  1576.     winstr$ = "-> Game paused <-"
  1577.     COLOR MSG_COL
  1578.     _PRINTSTRING ((game.maxx - LEN(winstr$) * 10) / 2, (game.maxy - 50) / 2), winstr$
  1579.  
  1580. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1581. SUB DrawBall (baln AS gula) 'draw ball
  1582.     CONST BSEGM = 10
  1583.     koef = (10 + 90 * game.printdebug) * FPS / 100
  1584.     bcol = baln.col
  1585.     IF baln.freezedtime > 0 THEN bcol = 27
  1586.     IF baln.riadena = 1 THEN
  1587.         CIRCLE (baln.x, baln.y), baln.r - 1, baln.col
  1588.         CIRCLE (baln.x, baln.y), baln.r + 1, baln.col
  1589.         FOR i = 1 TO BSEGM
  1590.             CIRCLE (baln.x, baln.y), i * (baln.r / 2 - 2) / BSEGM, bcol, 0, baln.energia / stav.energy_player_start * _PI * 2
  1591.         NEXT i
  1592.         LINE (baln.x, baln.y)-(baln.x + baln.v.x * koef, baln.y + baln.v.y * koef), baln.col 'draw actual velocity
  1593.         IF game.printdebug = 1 THEN LINE (baln.x, baln.y)-(baln.x + baln.vi.x * koef, baln.y + baln.vi.y * koef), bal(baln.protivnik).col 'draw intended velocity
  1594.     END IF
  1595.     IF baln.i = stav.baljeto THEN
  1596.         CIRCLE (baln.x, baln.y), baln.r * 2 / 3, 15
  1597.     END IF
  1598.     IF baln.i > stav.pocethracov THEN
  1599.         reflectfrom = ATN((1.25 * stav.maxy - baln.y) / (1.25 * stav.maxx - baln.x))
  1600.         CIRCLE (baln.x, baln.y), baln.r - 5, 28, reflectfrom, reflectfrom + _PI / 8
  1601.         CIRCLE (baln.x, baln.y), baln.r - 5, 28, reflectfrom + _PI / 8 + _PI / 32, reflectfrom + _PI / 4
  1602.         CIRCLE (baln.x, baln.y), baln.r - 10, 28, reflectfrom, reflectfrom + _PI / 8
  1603.         CIRCLE (baln.x, baln.y), baln.r - 10, 28, reflectfrom + _PI / 8 + _PI / 32, reflectfrom + _PI / 4
  1604.     END IF
  1605.  
  1606.     CIRCLE (baln.x, baln.y), baln.r, baln.col
  1607.  
  1608. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1609. SUB DrawScore 'Draw player score
  1610.     WINDOW (0, 0)-(game.maxx, game.maxy)
  1611.     FOR i = 1 TO stav.pocethracov
  1612.         scxl = SCORE_DELTA_X * -(game.maxx >= 4 * SCORE_DELTA_X) + game.maxx / 4 * -(game.maxx < 4 * SCORE_DELTA_X)
  1613.         dx = (game.maxx - 4 * scxl) / 2
  1614.         scx = dx + scxl * ((i - 4 * INT((i - 1) / 4)) - 1)
  1615.         scty = SCORE_DELTA_Y + 15 * INT((i - 1) / 4)
  1616.         scly = game.maxy - SCORE_DELTA_Y - 15 * INT((i - 1) / 4)
  1617.         COLOR bal(hrac(i).bal).col
  1618.         _PRINTSTRING (scx + (LEN(hrac(i).meno) - LEN(RTRIM$(hrac(i).meno))) * 8, scty - 10), RTRIM$(hrac(i).meno) + " -" + STR$(hrac(i).skore)
  1619.         LINE (scx + SCORE_NAME_LEN, scly)-(scx + SCORE_NAME_LEN + bal(hrac(i).bal).energia, scly + 7), bal(hrac(i).bal).col, BF
  1620.         IF bal(hrac(i).bal).energia <= 0 THEN
  1621.             _PRINTSTRING (scx + SCORE_NAME_LEN, scty - 10), "OUT"
  1622.         END IF
  1623.     NEXT i
  1624.     WINDOW (0, 0)-(stav.maxx, stav.maxy)
  1625.  
  1626. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1627. SUB CalculatePlayerState (baln AS gula) 'calculate players states
  1628.     DIM od AS SINGLE
  1629.     '-----------------------energy decreasing-------------------------------------
  1630.     IF baln.freezedtime > 0 THEN
  1631.         baln.freezedtime = baln.freezedtime - 1
  1632.     ELSE
  1633.         baln.energia = baln.energia - (stav.energydec * -(stav.baljeto = baln.i)) * game.fpscoef
  1634.     END IF
  1635.     IF baln.energia <= 0 THEN
  1636.         baln.riadena = 0
  1637.         pocetriadenych = 0
  1638.         FOR i = 1 TO stav.pocethracov
  1639.             IF bal(hrac(i).bal).riadena = 1 THEN
  1640.                 pocetriadenych = pocetriadenych + 1
  1641.                 priebeznyvitaz = i
  1642.             END IF
  1643.         NEXT i
  1644.         IF pocetriadenych > 1 THEN
  1645.             stav.baljeto = stav.balbudeto
  1646.             stav.balbudeto = nearestPlayer(bal(stav.baljeto), 0, 0)
  1647.             FOR i = 1 TO stav.pocetgulx
  1648.                 IF bal(i).i <> stav.baljeto AND bal(i).riadena = 1 THEN bal(i).protivnik = stav.baljeto
  1649.             NEXT i
  1650.         END IF
  1651.         IF pocetriadenych = 1 THEN
  1652.             stav.vitaz = priebeznyvitaz
  1653.             hrac(stav.vitaz).skore = hrac(stav.vitaz).skore + 1
  1654.             IF hrac(stav.vitaz).skore >= stav.winscore THEN game.autostart_time = TIMER + DELAY_MATCHEND ELSE game.autostart_time = TIMER + DELAY_ROUNDSTART
  1655.             DrawScore
  1656.         END IF
  1657.     END IF
  1658.     '-----------------------dodging time-----------------------------------------
  1659.     IF baln.klucka_time = 0 AND baln.klucka_trvanie = 0 AND baln.klucka_pocet = 0 THEN
  1660.         baln.klucka_time = INT(RND * strat(baln.strat).klucka_avgtm + strat(baln.strat).klucka_avgtm / 2) / game.fpscoef
  1661.         baln.klucka_pocet = strat(baln.strat).klucka_pocet
  1662.     END IF
  1663.     IF baln.klucka_time > 0 THEN
  1664.         baln.klucka_time = baln.klucka_time - 1
  1665.     END IF
  1666.     IF baln.klucka_time = 0 AND baln.klucka_trvanie > 0 THEN
  1667.         baln.klucka_trvanie = baln.klucka_trvanie - 1
  1668.     END IF
  1669.     IF baln.klucka_time = 0 AND baln.klucka_trvanie = 0 AND baln.klucka_pocet > 0 THEN
  1670.         baln.klucka_pocet = baln.klucka_pocet - 1
  1671.         baln.klucka_trvanie = INT(RND * strat(baln.strat).klucka_trvanie + strat(baln.strat).klucka_trvanie / 2) / game.fpscoef
  1672.         baln.klucka_uhol = RND * strat(baln.strat).klucka_uhol * (2 * strat(baln.strat).klucka_uholodch) + strat(baln.strat).klucka_uhol * (1 - strat(baln.strat).klucka_uholodch) * (3 - INT(RND * 2 + 1) * 2)
  1673.     END IF
  1674.     '-----------------------choose nearest enemy if tagged-----------------------
  1675.     IF stav.baljeto = baln.i THEN
  1676.         novyprotivnik = nearestPlayer(baln, 0, -(baln.energia > (stav.energy_player_start * strat(baln.strat).adrenalin_energy)))
  1677.         IF novyprotivnik <> 0 THEN baln.protivnik = novyprotivnik
  1678.         stav.balbudeto = nearestPlayer(baln, 0, 0)
  1679.     END IF
  1680.     '-----------------------boosting time----------------------------------------
  1681.     IF baln.boost_time > 0 AND baln.boost_trvanie = 0 THEN baln.boost_time = baln.boost_time - 1
  1682.     IF baln.boost_trvanie > 0 THEN baln.boost_trvanie = baln.boost_trvanie - 1
  1683.  
  1684.     '-----------------------reaction drop time-----------------------------------
  1685.     IF baln.reactdrop_time > 0 THEN baln.reactdrop_time = baln.reactdrop_time - 1
  1686.     IF baln.reactdrop_time <= 0 AND baln.reactdrop_dur > 0 THEN baln.reactdrop_dur = baln.reactdrop_dur - 1
  1687.     IF baln.reactdrop_time <= 0 AND baln.reactdrop_dur <= 0 THEN
  1688.         IF baln.energia < (stav.energy_player_start * strat(baln.strat).adrenalin_energy) THEN reactkoef = (1 - strat(baln.strat).adrenalin_rate) ELSE reactkoef = 1
  1689.         baln.reactdrop_time = INT((RND * strat(baln.strat).reactdrop_per * reactkoef + strat(baln.strat).reactdrop_per * reactkoef / 2) / game.fpscoef)
  1690.         baln.reactdrop_dur = INT((RND * strat(baln.strat).reactdrop_dur * reactkoef + strat(baln.strat).reactdrop_dur * reactkoef / 2) / game.fpscoef)
  1691.         baln.smer_odchyl = (RND * strat(baln.strat).chybasmeru - strat(baln.strat).chybasmeru / 2) * pi 'apply random radian deviance from intended direction
  1692.     END IF
  1693.  
  1694. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1695. FUNCTION nearestPlayer (baln AS gula, mateonly AS _BYTE, weighted AS _BYTE) 'returns nearest opponent idx
  1696.     CONST NEAREST_WEIGHTCOEF = 10.0 'coeficient for energy inclusion into weight
  1697.     DIM weight, vzd, vzdakt, vzdakt_winner AS SINGLE
  1698.     DIM nearest_winner AS INTEGER
  1699.     vzdakt = 0
  1700.     nearestPlayer = 0
  1701.     vzdakt_winner = 0
  1702.     nearest_winner = 0
  1703.     FOR i = 1 TO stav.pocetgulx
  1704.         IF bal(i).i <> baln.i AND bal(i).riadena = 1 AND (mateonly = 1 IMP i <> stav.baljeto) THEN
  1705.             weight = weighted * (stav.energy_player_start - bal(i).energia) * NEAREST_WEIGHTCOEF
  1706.             vzd = SQR((baln.x - bal(i).x) ^ 2 + (baln.y - bal(i).y) ^ 2) + weight
  1707.             IF nearestPlayer = 0 OR vzd < vzdakt THEN
  1708.                 nearestPlayer = i
  1709.                 vzdakt = vzd
  1710.             END IF
  1711.             IF hrac(bal(i).hrac).skore = stav.winscore - 1 AND (vzd < vzdakt_winner OR nearest_winner = 0) THEN
  1712.                 nearest_winner = i
  1713.                 vzdakt_winner = vzd
  1714.             END IF
  1715.         END IF
  1716.     NEXT i
  1717.     IF weighted = 1 AND nearest_winner > 0 THEN nearestPlayer = nearest_winner
  1718.  
  1719. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1720. SUB GenerateAIControl (baln AS gula) 'Decide computer player's control keys hits
  1721.     DIM dd AS SINGLE, vid AS SINGLE, vd AS SINGLE, eid AS SINGLE, vnd AS SINGLE, ved AS SINGLE, did AS SINGLE, thit AS SINGLE
  1722.     DIM rot_klucka AS SINGLE, rot_border AS SINGLE, rot_enemy AS SINGLE, rot_mate AS SINGLE
  1723.     DIM siner AS SINGLE, coser AS SINGLE, sineri AS SINGLE, coseri AS SINGLE
  1724.     DIM sinevr AS SINGLE, cosevr AS SINGLE, sinmt AS SINGLE, cosnmt AS SINGLE
  1725.     DIM okrajrot AS SINGLE, enemy_dist_factor AS SINGLE, smerdookraju AS INTEGER, nearmate AS INTEGER
  1726.     DIM d AS vektor, vi AS vektor, ei AS vektor, ve AS vektor, di AS vektor, dm AS vektor
  1727.  
  1728.     vi = baln.vi 'player's intended speed vector copy
  1729.     vid = SQR(vi.x ^ 2 + vi.y ^ 2)
  1730.     vi.x = vi.x / vid * stav.vmax
  1731.     vi.y = vi.y / vid * stav.vmax
  1732.     vd = SQR(baln.v.x ^ 2 + baln.v.y ^ 2)
  1733.     ve = bal(baln.protivnik).v 'enemy speed vector copy
  1734.     ved = SQR(ve.x ^ 2 + ve.y ^ 2)
  1735.  
  1736.     d.x = bal(baln.protivnik).x - baln.x 'player to enemy postition vector
  1737.     d.y = bal(baln.protivnik).y - baln.y
  1738.     dd = SQR(d.x ^ 2 + d.y ^ 2)
  1739.     coser = (d.x * baln.v.x + d.y * baln.v.y) / (dd * vd)
  1740.     siner = (d.x * baln.v.y - d.y * baln.v.x) / (dd * vd)
  1741.     cosevr = (ve.x * baln.v.x + ve.y * baln.v.y) / (ved * vd)
  1742.     sinevr = (ve.x * baln.v.y - ve.y * baln.v.x) / (ved * vd)
  1743.  
  1744.     '!EXPERIMENTAL CODE! - try more exact enemy future position vector, but  does not work so well as used approximation
  1745.     'di.x = d.x + ve.x * d.x / ((((d.x / dd) + (ve.x / ved)) * stav.vmax) - ve.x) 'new intended direction to enemy
  1746.     'di.y = d.y + ve.y * d.y / ((((d.y / dd) + (ve.y / ved)) * stav.vmax) - ve.y) 'new intended direction to enemy
  1747.     '!EXPERIMENTAL CODE! end
  1748.  
  1749.     di.x = d.x + ve.x * dd / vd * ABS(siner) 'player to enemy approximate future position vector, future enemy position more relevant when enemy closer
  1750.     di.y = d.y + ve.y * dd / vd * ABS(siner)
  1751.     did = SQR(di.x ^ 2 + di.y ^ 2)
  1752.     coseri = (di.x * baln.v.x + di.y * baln.v.y) / (did * vd)
  1753.     sineri = (di.x * baln.v.y - di.y * baln.v.x) / (did * vd)
  1754.  
  1755.     rot_enemy = 0 'rotation away from enemy in rad
  1756.     rot_border = 0 'rotation away from border in rad
  1757.     rot_mate = 0 'rotation towards nearest mate in rad
  1758.     rot_odchyl = 0 'baln.smer_odchyl * -(baln.reactdrop_dur = 1) 'apply direction error if react time
  1759.     smerdookraju = 0 'is players direction to border
  1760.  
  1761.     IF NOT (stav.baljeto = baln.i) THEN 'decide flee action
  1762.         enemy_dist_factor = 1 * -(dd <= strat(baln.strat).detekcia_vzdfull) + (1 - (dd - strat(baln.strat).detekcia_vzdfull) / _
  1763.           (strat(baln.strat).detekcia_vzdmin - strat(baln.strat).detekcia_vzdfull)) * -(dd > strat(baln.strat).detekcia_vzdfull AND dd <= strat(baln.strat).detekcia_vzdmin)' factor=1 if enemy distance within treshold, then decreasing
  1764.  
  1765.         IF enemy_dist_factor > 0 THEN 'if enemy in detection distance, rotate away
  1766.             rot_enemy = -(coseri > 0) * -(ABS(sineri) <= strat(baln.strat).detekcia_sin) * (-(sineri > 0) + (sineri < 0)) * strat(baln.strat).detekcia_rot * enemy_dist_factor * game.fpscoef
  1767.  
  1768.             '!EXPERIMENTAL CODE! - try better rotation from enemy
  1769.             'strat(baln.strat).detekcia_rot = 10 * pi / FPS 'TEMPORARY MODIFICATION
  1770.             'cosevi = (di.x * vi.x + di.y * vi.y) / (did * vid)
  1771.             'sinevi = (di.x * vi.y - di.y * vi.x) / (did * vid)
  1772.             'cosvvi = (v.x * vi.x + v.y * vi.y) / (vd * vid)
  1773.             'sinvvi = (v.x * vi.y - v.y * vi.x) / (vd * vid)
  1774.             'IF (coseri > 0) AND (ABS(sineri) <= strat(baln.strat).detekcia_sin) THEN
  1775.             '    rot_dir = -(sineri > 0) + (sineri < 0)
  1776.             '    IF NOT ((cosevi > 0 AND (ABS(sinevi) <= strat(baln.strat).detekcia_sin)) AND NOT (rot_dir = (-(sinevi > 0) + (sinevi < 0)))) THEN
  1777.             '        rot_enemy = rot_dir * strat(baln.strat).detekcia_rot * enemy_dist_factor * game.fpscoef
  1778.             '    END IF
  1779.             'END IF
  1780.             '!EXPERIMENTAL CODE! end
  1781.  
  1782.         END IF
  1783.  
  1784.         okrajrot = strat(baln.strat).okraj_rot 'border unit rotation in rad
  1785.         IF baln.x < strat(baln.strat).okraj AND vi.x < 0 THEN
  1786.             rot_border = (okrajrot * (vi.y >= 0) + okrajrot * -(vi.y < 0)) '* (1 - enemy_dist_factor)
  1787.             smerdookraju = 1
  1788.         END IF
  1789.         IF baln.x > stav.maxx - strat(baln.strat).okraj AND vi.x > 0 THEN
  1790.             rot_border = (okrajrot * -(vi.y >= 0) + okrajrot * (vi.y < 0)) '* (1 - enemy_dist_factor)
  1791.             smerdookraju = 1
  1792.         END IF
  1793.         IF baln.y < strat(baln.strat).okraj AND vi.y < 0 THEN
  1794.             rot_border = (okrajrot * (vi.x >= 0) + okrajrot * (vi.x < 0)) '* (1 - enemy_dist_factor)
  1795.             smerdookraju = 1
  1796.         END IF
  1797.         IF baln.y > stav.maxy - strat(baln.strat).okraj AND vi.y > 0 THEN
  1798.             rot_border = (okrajrot * (vi.x >= 0) + okrajrot * -(vi.x < 0)) '* (1 - enemy_dist_factor)
  1799.             smerdookraju = 1
  1800.         END IF
  1801.     END IF
  1802.  
  1803.     rot_klucka = baln.klucka_uhol * -(baln.klucka_time = 0) * -(baln.klucka_trvanie = 1) * -(stav.baljeto <> baln.i) * _
  1804.       (dd > strat(baln.strat).mate_distmin OR (coseri < 0 AND sineri >= 0 AND baln.klucka_uhol > 0 AND baln.klucka_uhol < pi) OR (coseri < 0 AND sineri <= 0 AND baln.klucka_uhol < 0 AND baln.klucka_uhol > -pi)) 'apply dodge if dodging time - TEMPORARY DISABLED!!!
  1805.  
  1806.  
  1807.     IF baln.i = bal(stav.baljeto).protivnik THEN 'if ball being chased
  1808.         nearmate = nearestPlayer(baln, 1, -(baln.energia > (stav.energy_player_start * strat(baln.strat).adrenalin_energy)))
  1809.         IF nearmate > 0 THEN
  1810.             dm.x = bal(nearmate).x - baln.x
  1811.             dm.y = bal(nearmate).y - baln.y
  1812.             dmd = SQR(dm.x ^ 2 + dm.y ^ 2)
  1813.             cosnm = (dm.x * baln.v.x + dm.y * baln.v.y) / (dmd * vd)
  1814.             sinnm = (dm.x * baln.v.y - dm.y * baln.v.x) / (dmd * vd)
  1815.             IF (rot_border = 0) AND (dmd > strat(baln.strat).mate_distmin) AND (((coseri < strat(baln.strat).mate_cosenmax) AND (dd < strat(baln.strat).mate_en_distmin)) OR ((coseri < 0) AND (dd > strat(baln.strat).mate_en_distmin))) THEN
  1816.                 rot_mate = ((sinnm > 0) * strat(baln.strat).mate_rot * game.fpscoef - (sinnm <= 0) * strat(baln.strat).mate_rot * game.fpscoef) 'rotate towards mate
  1817.             END IF
  1818.         END IF
  1819.     END IF
  1820.  
  1821.     RotateVector vi, rot_border + rot_klucka + rot_odchyl + rot_enemy + rot_mate 'aplly all calculated rotations to intended velocity
  1822.  
  1823.     IF stav.baljeto = baln.i THEN 'chasing direction intended
  1824.         IF baln.reactdrop_time > 0 THEN 'if time is up for reaction
  1825.             baln.boostintended = -(-(coser > 0) AND -(cosevr > 0) AND -(ABS(sinevr) < strat(baln.strat).boost_chasesin) AND baln.boost_time = 0 AND smerdookraju = 0)
  1826.             baln.vi.x = stav.vmax * di.x / did
  1827.             baln.vi.y = stav.vmax * di.y / did
  1828.         END IF
  1829.     ELSE 'flee direction intended
  1830.         baln.boostintended = -(-(coser < 0) AND baln.boost_time = 0 AND dd < (RND * strat(baln.strat).boost_dist + 2 * PLAY_BALL_R) AND smerdookraju = 0)
  1831.         baln.vi.x = vi.x
  1832.         baln.vi.y = vi.y
  1833.     END IF
  1834.  
  1835. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1836. SUB ReadGamepad (klavn AS tklavesy) 'read state of gamepad
  1837.     di = _DEVICEINPUT(klavn.devid)
  1838.     IF klavn.typ = 3 THEN
  1839.         klavn.gvektor.x = _AXIS(klavn.xaxis)
  1840.         klavn.gvektor.y = -_AXIS(klavn.yaxis)
  1841.         klavn.isshoota = _BUTTON(klavn.shoota)
  1842.         klavn.isshootb = _BUTTON(klavn.shootb)
  1843.         klavn.isup = (_AXIS(klavn.dyaxis) = -1)
  1844.         klavn.isdown = (_AXIS(klavn.dyaxis) = 1)
  1845.         klavn.isleft = (_AXIS(klavn.dxaxis) = -1)
  1846.         klavn.isright = (_AXIS(klavn.dxaxis) = 1)
  1847.         IF klavn.gvektor.x > 0.5 THEN klavn.isright = -1
  1848.         IF klavn.gvektor.x < -0.5 THEN klavn.isleft = -1
  1849.         IF klavn.gvektor.y > 0.5 THEN klavn.isup = -1
  1850.         IF klavn.gvektor.y < -0.5 THEN klavn.isdown = -1
  1851.  
  1852.         IF klavn.buttonstate <> (klavn.isshoota + klavn.isshootb * 2 + klavn.isup * 4 + klavn.isdown * 8 + klavn.isright * 16 + klavn.isleft * 32) THEN
  1853.             klavn.buttonstatechanged = -1
  1854.             klavn.buttonstatetimer = 50
  1855.         ELSE
  1856.             klavn.buttonstatechanged = 0
  1857.         END IF
  1858.         klavn.buttonstate = (klavn.isshoota + klavn.isshootb * 2 + klavn.isup * 4 + klavn.isdown * 8 + klavn.isright * 16 + klavn.isleft * 32)
  1859.     END IF
  1860.     IF klavn.buttonstatetimer > 0 THEN klavn.buttonstatetimer = klavn.buttonstatetimer - 1
  1861.     IF klavn.buttonstatetimer = 0 THEN klavn.buttonstatechanged = -1
  1862.  
  1863. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1864. SUB ResolvePlayerControl (baln AS gula, klavn AS tklavesy) 'perform players control keys actions
  1865.     DIM vn AS vektor, gvector AS vektor
  1866.  
  1867.     IF baln.complayer = 0 THEN 'read human player controls
  1868.         IF klavn.typ = 1 THEN 'read keyboard control
  1869.             klavn.isright = _KEYDOWN(klavn.right)
  1870.             klavn.isleft = _KEYDOWN(klavn.left)
  1871.             klavn.isdown = _KEYDOWN(klavn.down)
  1872.             klavn.isup = _KEYDOWN(klavn.up)
  1873.             klavn.isshoota = _KEYDOWN(klavn.shoota)
  1874.             klavn.isshootb = _KEYDOWN(klavn.shootb)
  1875.             baln.vi.x = baln.v.x
  1876.             baln.vi.y = baln.v.y
  1877.         END IF
  1878.  
  1879.         IF klavn.typ = 3 THEN 'read gamepad control
  1880.             baln.vi.x = 0
  1881.             baln.vi.y = 0
  1882.             ReadGamepad klavn
  1883.             baln.vi.x = klavn.gvektor.x * stav.vmax
  1884.             baln.vi.y = klavn.gvektor.y * stav.vmax
  1885.             IF ABS(klavn.gvektor.x) > GAMESTICK_DEADZONE OR ABS(klavn.gvektor.y) > GAMESTICK_DEADZONE THEN
  1886.                 klavn.isright = (baln.v.x < baln.vi.x) 'set control keys status for desired direction
  1887.                 klavn.isleft = (baln.v.x > baln.vi.x)
  1888.                 klavn.isup = (baln.v.y < baln.vi.y)
  1889.                 klavn.isdown = (baln.v.y > baln.vi.y)
  1890.             END IF
  1891.             IF klavn.isshootb AND game.isnetgame = 0 THEN stav.koniec_kola = 2
  1892.         END IF
  1893.  
  1894.         IF klavn.typ = 5 AND game.isnetgame = 1 THEN
  1895.             IF game.clienth <> 0 THEN ' reset network controller state on client
  1896.                 klavn.isright = (baln.v.x < baln.vi.x) 'set control keys status for desired direction
  1897.                 klavn.isleft = (baln.v.x > baln.vi.x)
  1898.                 klavn.isup = (baln.v.y < baln.vi.y)
  1899.                 klavn.isdown = (baln.v.y > baln.vi.y)
  1900.                 'klavn.isright = 0
  1901.                 'klavn.isleft = 0
  1902.                 'klavn.isup = 0
  1903.                 'klavn.isdown = 0
  1904.                 klavn.isshoota = 0
  1905.             END IF
  1906.             baln.vi.x = baln.v.x
  1907.             baln.vi.y = baln.v.y
  1908.         END IF
  1909.  
  1910.     ELSE 'get controls of computer player
  1911.         IF baln.riadena = 1 THEN GenerateAIControl baln
  1912.         klavn.isright = (baln.v.x < baln.vi.x) 'set control keys status for desired direction
  1913.         klavn.isleft = (baln.v.x > baln.vi.x)
  1914.         klavn.isup = (baln.v.y < baln.vi.y)
  1915.         klavn.isdown = (baln.v.y > baln.vi.y)
  1916.         klavn.isshoota = -baln.boostintended
  1917.     END IF
  1918.  
  1919.     IF baln.freezedtime <= 0 AND baln.riadena = 1 THEN 'apply acquired controls to player movement
  1920.         vn.x = baln.v.x
  1921.         vn.y = baln.v.y
  1922.  
  1923.         game.fpscoef = stav.fps / game.fpsactual
  1924.         IF klavn.isright THEN vn.x = vn.x + baln.a.x * game.fpscoef 'accelerate player according to control keys states
  1925.         IF klavn.isleft THEN vn.x = vn.x - baln.a.x * game.fpscoef
  1926.         IF klavn.isdown THEN vn.y = vn.y - baln.a.y * game.fpscoef
  1927.         IF klavn.isup THEN vn.y = vn.y + baln.a.x * game.fpscoef
  1928.         IF klavn.isshoota AND baln.boost_time = 0 AND stav.boostallowed = 1 THEN 'set boost state
  1929.             baln.boost_trvanie = BOOST_TRVANIE / game.fpscoef
  1930.             baln.boost_time = BOOST_TIME / game.fpscoef
  1931.         END IF
  1932.         vnd = SQR(vn.x ^ 2 + vn.y ^ 2) 'limit actual velocity to max. velocity
  1933.         IF vnd > stav.vmax THEN
  1934.             vn.x = vn.x * stav.vmax / vnd
  1935.             vn.y = vn.y * stav.vmax / vnd
  1936.         END IF
  1937.  
  1938.         IF baln.boost_trvanie > 0 THEN 'add some extra speed if boosted
  1939.             vn.x = vn.x + stav.vmax * BOOST_V_KOEF * (baln.boost_trvanie / BOOST_TRVANIE) * game.fpscoef * vn.x / vnd
  1940.             vn.y = vn.y + stav.vmax * BOOST_V_KOEF * (baln.boost_trvanie / BOOST_TRVANIE) * game.fpscoef * vn.y / vnd
  1941.         END IF
  1942.  
  1943.         baln.v.x = vn.x 'apply controler modified velocity
  1944.         baln.v.y = vn.y
  1945.     END IF
  1946.  
  1947. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1948. SUB PerformMovement (baln AS gula) 'Calculate ball movement
  1949.     baln.x = baln.x + baln.v.x * game.fpscoef
  1950.     baln.y = baln.y + baln.v.y * game.fpscoef
  1951.     baln.v.x = baln.v.x * stav.odpor ^ game.fpscoef
  1952.     baln.v.y = baln.v.y * stav.odpor ^ game.fpscoef
  1953.     IF (baln.x + baln.r > stav.maxx) THEN
  1954.         baln.v.x = -baln.v.x
  1955.         baln.x = stav.maxx - baln.r
  1956.     END IF
  1957.     IF (baln.x - baln.r < stav.minx) THEN
  1958.         baln.v.x = -baln.v.x
  1959.         baln.x = stav.minx + baln.r
  1960.     END IF
  1961.     IF (baln.y + baln.r > stav.maxy) THEN
  1962.         baln.v.y = -baln.v.y
  1963.         baln.y = stav.maxy - baln.r
  1964.     END IF
  1965.     IF (baln.y - baln.r < stav.miny) THEN
  1966.         baln.v.y = -baln.v.y
  1967.         baln.y = stav.miny + baln.r
  1968.     END IF
  1969.  
  1970. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1971. SUB BounceTest (bala AS gula, balb AS gula) 'Balls bounce test
  1972.     IF (bala.x - balb.x) * (bala.x - balb.x) + (bala.y - balb.y) * (bala.y - balb.y) <= (bala.r + balb.r + 0) * (bala.r + balb.r + 0) THEN
  1973.         PerformBounce bala, balb
  1974.         IF bala.riadena AND balb.riadena AND bala.freezedtime <= 0 AND balb.freezedtime <= 0 AND (stav.baljeto = bala.i OR stav.baljeto = balb.i) THEN 'balb.col = bal(bala.protivnik).col
  1975.             newjeto = bala.i * -(stav.baljeto <> bala.i) + balb.i * -(stav.baljeto <> balb.i)
  1976.             IF stav.baljeto <= PLAYER_NET_MAX AND newjeto <= PLAYER_NET_MAX AND (game.isnetgame = 0 OR game.hosth <> 0) THEN
  1977.                 stats(stav.baljeto, newjeto) = stats(stav.baljeto, newjeto) + 1
  1978.                 game.sync_stats = 1
  1979.             END IF
  1980.             stav.baljeto = newjeto
  1981.             bala.freezedtime = -(stav.baljeto = bala.i) * FREEZE_TIME / game.fpscoef
  1982.             balb.freezedtime = -(stav.baljeto = balb.i) * FREEZE_TIME / game.fpscoef
  1983.             bala.protivnik = balb.i
  1984.             balb.protivnik = bala.i
  1985.             FOR i = 1 TO stav.pocetgulx
  1986.                 IF bal(i).i <> stav.baljeto AND bal(i).riadena = 1 THEN bal(i).protivnik = bal(stav.baljeto).i
  1987.             NEXT i
  1988.         END IF
  1989.     END IF
  1990.  
  1991. '----------------------------------------------------------------------------------------------------------------------------------------------------
  1992. SUB PerformBounce (bala AS gula, balb AS gula) 'Calculate balls bounce
  1993.     DIM dx AS SINGLE, dy AS SINGLE, d AS SINGLE
  1994.     DIM l AS SINGLE, l1 AS SINGLE, l2 AS SINGLE
  1995.     DIM k AS SINGLE, k1 AS SINGLE, k2 AS SINGLE
  1996.     DIM m AS SINGLE, m1 AS SINGLE, m2 AS SINGLE
  1997.     DIM n AS SINGLE, n1 AS SINGLE, n2 AS SINGLE
  1998.     DIM cosa AS SINGLE, cosb AS SINGLE, p AS SINGLE
  1999.     DIM ma AS SINGLE, mb AS SINGLE
  2000.  
  2001.     IF stav.massallowed = 1 THEN
  2002.         ma = bala.m
  2003.         mb = balb.m
  2004.     ELSE
  2005.         ma = 1
  2006.         mb = 1
  2007.     END IF
  2008.  
  2009.     dx = balb.x - bala.x
  2010.     dy = balb.y - bala.y
  2011.     d = SQR(dx ^ 2 + dy ^ 2)
  2012.  
  2013.     p = bala.r + balb.r - d 'Move overlapping balls away from each other
  2014.     IF p >= 0 THEN
  2015.         bala.x = bala.x - dx * p / d / 2 * 2
  2016.         bala.y = bala.y - dy * p / d / 2 * 2
  2017.         balb.x = balb.x + dx * p / d / 2 * 2
  2018.         balb.y = balb.y + dy * p / d / 2 * 2
  2019.  
  2020.         dx = balb.x - bala.x
  2021.         dy = balb.y - bala.y
  2022.         d = SQR(dx ^ 2 + dy ^ 2)
  2023.     END IF
  2024.  
  2025.     cosa = dy / d
  2026.     cosb = -cosa
  2027.  
  2028.     l = (dx * bala.v.x * ma + dy * bala.v.y * ma) / d
  2029.     l2 = l * cosa
  2030.     k = (dy * bala.v.x * ma - dx * bala.v.y * ma) / d
  2031.     k1 = k * cosa
  2032.     l1 = bala.v.x * ma - k1
  2033.     k2 = bala.v.y * ma - l2
  2034.  
  2035.     n = (-dx * balb.v.x * mb - dy * balb.v.y * mb) / d
  2036.     n2 = n * cosb
  2037.     m = (-dy * balb.v.x * mb + dx * balb.v.y * mb) / d
  2038.     m1 = m * cosb
  2039.     n1 = balb.v.x * mb - m1
  2040.     m2 = balb.v.y * mb - n2
  2041.  
  2042.     bala.v.x = (k1 + n1) / ma
  2043.     bala.v.y = (k2 + n2) / ma
  2044.     balb.v.x = (m1 + l1) / mb
  2045.     balb.v.y = (m2 + l2) / mb
  2046.  
  2047. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2048. FUNCTION MATHROUND (n)
  2049.     MATHROUND = FIX(n + 0.5 * SGN(n))
  2050.  
  2051. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2052. SUB RotateVector (vn AS vektor, b AS SINGLE)
  2053.     DIM vt AS vektor
  2054.     sinb = SIN(b): cosb = COS(b)
  2055.     vt.x = vn.x * cosb - vn.y * sinb
  2056.     vt.y = vn.y * cosb + vn.x * sinb
  2057.     vn = vt
  2058.  
  2059. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2060. SUB CalculateFps 'calculate actual fps coeficient for correct timig
  2061.     IF game.tend = game.tstart THEN
  2062.         game.fpsactual = stav.fps
  2063.     ELSE
  2064.         game.fpsactual = ((1 / (game.tend - game.tstart)) * 1 + game.fpsactual * 99) / 100
  2065.     END IF
  2066.     game.tstart = game.tend
  2067.     game.fpscoef = stav.fps / game.fpsactual
  2068.  
  2069. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2070. SUB DisplayMessages
  2071.     IF game.msgcount > 0 THEN 'refresh messages validity
  2072.         FOR i = 1 TO game.msgcount
  2073.             IF ((TIMER(.001) - msg(i).time_start) > msg(i).time_dur) AND msg(i).time_dur > 0 THEN DeleteMessage (i)
  2074.         NEXT i
  2075.     END IF
  2076.     LOCATE 3, 1
  2077.     IF game.msgcount > 0 THEN 'print messages
  2078.         FOR i = 1 TO game.msgcount
  2079.             COLOR msg(i).col
  2080.             PRINT RTRIM$(msg(i).text)
  2081.         NEXT i
  2082.     END IF
  2083.  
  2084. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2085. SUB AddMessage (mtext AS STRING, mcol AS INTEGER, mdur AS SINGLE, mdist AS INTEGER)
  2086.     IF LEFT$(mtext, 1) = "/" THEN
  2087.         TextCommand mtext
  2088.     ELSE
  2089.         IF mdur <> 0 THEN
  2090.             IF game.msgcount >= MSG_COUNT_MAX THEN DeleteMessage (1) 'delete last message if full
  2091.             game.msgcount = game.msgcount + 1
  2092.             msg(game.msgcount).clientid = game.clientid
  2093.             msg(game.msgcount).text = mtext
  2094.             msg(game.msgcount).col = mcol
  2095.             msg(game.msgcount).time_dur = mdur
  2096.             msg(game.msgcount).time_start = TIMER(.001)
  2097.             msg(game.msgcount).syncstr = "SNM"
  2098.             msg(game.msgcount).synctime = 0
  2099.             msg(game.msgcount).distribute = mdist
  2100.             IF mdist = 1 THEN game.newmsg = 1
  2101.         END IF
  2102.     END IF
  2103.  
  2104. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2105. SUB DeleteMessage (mid AS INTEGER)
  2106.     IF mid < game.msgcount AND mid > 0 THEN
  2107.         FOR i = mid TO game.msgcount - 1
  2108.             msg(i) = msg(i + 1)
  2109.         NEXT i
  2110.     END IF
  2111.     game.msgcount = game.msgcount - 1
  2112.  
  2113. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2114. SUB TextCommand (ctext AS STRING)
  2115.     SELECT CASE LEFT$(RTRIM$(ctext), 2)
  2116.         CASE "/q" 'end network game
  2117.             IF game.hosth <> 0 AND stav.gameplay = 1 THEN
  2118.                 stav.koniec_kola = 2
  2119.                 AddMessage "The match has been ended by the host", MSG_COL, MSG_DUR_STD, 1
  2120.             END IF
  2121.             IF game.clienth <> 0 AND stav.gameplay = 1 THEN
  2122.                 stav.koniec_kola = 2
  2123.                 ResetNetGame
  2124.             END IF
  2125.         CASE "/d" 'disconnet client clidx
  2126.             clidx = VAL(MID$(RTRIM$(ctext), 3, LEN(RTRIM$(ctext)) - 2))
  2127.             IF game.hosth <> 0 AND clidx > 1 AND clidx <= PLAYER_NET_MAX THEN
  2128.                 IF client(clidx).conn <> 0 THEN
  2129.                     CloseClientConn clidx
  2130.                     AddMessage "Player " + RTRIM$(hrac(clidx).meno) + " was disconnected by host", MSG_COL, MSG_DUR_STD, 1
  2131.                 END IF
  2132.             END IF
  2133.     END SELECT
  2134.  
  2135. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2136. SUB DebugScreen (K AS INTEGER) 'print debyg information on screen in runtime
  2137.     COLOR 7
  2138.     LOCATE INT((stav.pocethracov - 0.5) / 4) + 2, 1
  2139.     PRINT "FPS:   "; game.fpsactual
  2140.  
  2141.     IF K = 101 THEN stav.energydec = -(stav.energydec = 0) * ENERGY_DEC
  2142.     IF K = 113 THEN
  2143.         bal(hrac(2).bal).strat = bal(hrac(2).bal).strat + 1
  2144.         IF bal(hrac(2).bal).strat > UBOUND(strat) THEN
  2145.             bal(hrac(2).bal).strat = 1
  2146.         END IF
  2147.     END IF
  2148.     COLOR 13
  2149.     PRINT "hrac(n)..isleft   "; klav(hrac(1).klavesy).isleft; klav(hrac(2).klavesy).isleft; klav(hrac(3).klavesy).isleft; klav(hrac(4).klavesy).isleft
  2150.     PRINT "hrac(n)..isright  "; klav(hrac(1).klavesy).isright; klav(hrac(2).klavesy).isright; klav(hrac(3).klavesy).isright; klav(hrac(4).klavesy).isright
  2151.     PRINT "hrac(n)..isdown   "; klav(hrac(1).klavesy).isdown; klav(hrac(2).klavesy).isdown; klav(hrac(3).klavesy).isdown; klav(hrac(4).klavesy).isdown
  2152.     PRINT "hrac(n)..isup     "; klav(hrac(1).klavesy).isup; klav(hrac(2).klavesy).isup; klav(hrac(3).klavesy).isup; klav(hrac(4).klavesy).isup
  2153.     PRINT "bal(2).vi        "; bal(hrac(2).bal).vi.x; bal(hrac(2).bal).vi.y
  2154.     PRINT "bal(3).vi        "; bal(hrac(3).bal).vi.x; bal(hrac(3).bal).vi.y
  2155.     PRINT "bal(4).vi        "; bal(hrac(4).bal).vi.x; bal(hrac(4).bal).vi.y
  2156.     PRINT "bal(2).strat     "; bal(2).strat
  2157.     PRINT "bal(2).klucka_time    "; bal(hrac(2).bal).klucka_time
  2158.     PRINT "bal(2).klucka_pocet   "; bal(hrac(2).bal).klucka_pocet
  2159.     PRINT "bal(2).klucka_trvanie "; bal(hrac(2).bal).klucka_trvanie
  2160.     PRINT "bal(2).klucka_uhol    "; bal(hrac(2).bal).klucka_uhol
  2161.     PRINT "bal(n).protivnik      "; bal(1).protivnik; bal(2).protivnik; bal(3).protivnik
  2162.     PRINT "bal(n).complayer      "; bal(1).protivnik; bal(2).protivnik; bal(3).protivnik
  2163.     PRINT "stav.baljeto          "; stav.baljeto
  2164.     PRINT "stav.koniec_kola      "; stav.koniec_kola
  2165.     PRINT "nearestPlayer(1)      "; nearestPlayer(bal(1), 0, 1)
  2166.     PRINT "bal(n).reactdrop_time _dur "; bal(2).reactdrop_time; bal(2).reactdrop_dur
  2167.     PRINT "bal(1).v              "; bal(1).v.x; bal(1).v.y
  2168.     PRINT "bal(1).a              "; bal(1).a.x; bal(1).a.y
  2169.     PRINT "bal(2).boostintended  "; bal(2).boostintended
  2170.     PRINT "bal(2).boost_time     "; bal(1).boost_time
  2171.     PRINT "bal(2).boost_trvanie  "; bal(1).boost_trvanie
  2172.     PRINT "bal(n).hrac           "; bal(1).hrac; bal(2).hrac; bal(3).hrac; bal(4).hrac
  2173.     PRINT "bal(1).syncstr        "; bal(1).syncstr
  2174.     PRINT "game.autostart_time   "; game.autostart_time
  2175.     PRINT "stav.autostart_set    "; stav.autostart_set
  2176.     PRINT "TIMER                 "; TIMER
  2177.     PRINT "client(n).conn"; client(1).conn; client(2).conn; client(3).conn; client(4).conn
  2178.     PRINT "hrac(n).klavesy"; hrac(1).klavesy; hrac(2).klavesy; hrac(3).klavesy; hrac(4).klavesy; hrac(10).klavesy
  2179.     PRINT "klav(hrac(n)klavesy).typ"; klav(hrac(1).klavesy).typ; klav(hrac(2).klavesy).typ; klav(hrac(3).klavesy).typ; klav(hrac(4).klavesy).typ; klav(hrac(10).klavesy).typ
  2180.     PRINT "game.sync_stats         "; game.sync_stats
  2181.  
  2182. '----------------------------------------------------------------------------------------------------------------------------------------------------
  2183.  
  2184.  


Game description:
The aim is simple - do not allow to get tagged. Tagged player loses energy. If you get tagged, tag someone else as quickly as possible. When you are out of energy, you are out of the game round. Win 3 rounds to win the whole match.

Network game:
Host player chooses "Host game" from the main menu. Other players launch the game on their computers,  choose "Join game" from main menu and input the IP address (or URL) of the computer with the host game running.
Player 1 is allways assigned to the host. Other players can be joined client players, other local players on host computer or AI players.

Control keys during the match:
P - pause the game
B - enter debug mode
E - enable/disable energy decreasing (only in debug mode)
<Esc> - quit the game (only if not in network game mode)
T - write text message to other players (only while hosting or joining a network session)
T/q - quit the game (only if hosting or joining a network session)
T/d2 - disconnect player 2 from session (only if hosting a network session)