diff options
| author | Amir Rajan <[email protected]> | 2020-09-11 02:02:01 -0500 |
|---|---|---|
| committer | Amir Rajan <[email protected]> | 2020-09-11 02:02:57 -0500 |
| commit | 33ec37b141e896b47ed642923fd33b0c658ae9fb (patch) | |
| tree | a40d3e5d41beeb06508200078f6f26b0ee57d6a4 /samples/04_physics_and_collisions/04_box_collision | |
| parent | 958cf43779d2bf528869e80511c4c4f2a433b2db (diff) | |
| download | dragonruby-game-toolkit-contrib-33ec37b141e896b47ed642923fd33b0c658ae9fb.tar.gz dragonruby-game-toolkit-contrib-33ec37b141e896b47ed642923fd33b0c658ae9fb.zip | |
synced samples
Diffstat (limited to 'samples/04_physics_and_collisions/04_box_collision')
3 files changed, 564 insertions, 0 deletions
diff --git a/samples/04_physics_and_collisions/04_box_collision/app/main.rb b/samples/04_physics_and_collisions/04_box_collision/app/main.rb new file mode 100644 index 0000000..af85fef --- /dev/null +++ b/samples/04_physics_and_collisions/04_box_collision/app/main.rb @@ -0,0 +1,337 @@ +=begin + + APIs listing that haven't been encountered in previous sample apps: + + - first: Returns the first element of the array. + For example, if we have an array + numbers = [1, 2, 3, 4, 5] + and we call first by saying + numbers.first + the number 1 will be returned because it is the first element of the numbers array. + + - num1.idiv(num2): Divides two numbers and returns an integer. + For example, + 16.idiv(3) = 5, because 16 / 3 is 5.33333 returned as an integer. + 16.idiv(4) = 4, because 16 / 4 is 4 and already has no decimal. + + Reminders: + + - find_all: Finds all values that satisfy specific requirements. + + - ARRAY#intersect_rect?: An array with at least four values is + considered a rect. The intersect_rect? function returns true + or false depending on if the two rectangles intersect. + + - reject: Removes elements from a collection if they meet certain requirements. + +=end + +# This sample app allows users to create tiles and place them anywhere on the screen as obstacles. +# The player can then move and maneuver around them. + +class PoorManPlatformerPhysics + attr_accessor :grid, :inputs, :state, :outputs + + # Calls all methods necessary for the app to run successfully. + def tick + defaults + render + calc + process_inputs + end + + # Sets default values for variables. + # The ||= sign means that the variable will only be set to the value following the = sign if the value has + # not already been set before. Intialization happens only in the first frame. + def defaults + state.tile_size = 64 + state.gravity = -0.2 + state.previous_tile_size ||= state.tile_size + state.x ||= 0 + state.y ||= 800 + state.dy ||= 0 + state.dx ||= 0 + state.world ||= [] + state.world_lookup ||= {} + state.world_collision_rects ||= [] + end + + # Outputs solids and borders of different colors for the world and collision_rects collections. + def render + + # Sets a black background on the screen (Comment this line out and the background will become white.) + # Also note that black is the default color for when no color is assigned. + outputs.solids << grid.rect + + # The position, size, and color (white) are set for borders given to the world collection. + # Try changing the color by assigning different numbers (between 0 and 255) to the last three parameters. + outputs.borders << state.world.map do |x, y| + [x * state.tile_size, + y * state.tile_size, + state.tile_size, + state.tile_size, 255, 255, 255] + end + + # The top, bottom, and sides of the borders for collision_rects are different colors. + outputs.borders << state.world_collision_rects.map do |e| + [ + [e[:top], 0, 170, 0], # top is a shade of green + [e[:bottom], 0, 100, 170], # bottom is a shade of greenish-blue + [e[:left_right], 170, 0, 0], # left and right are a shade of red + ] + end + + # Sets the position, size, and color (a shade of green) of the borders of only the player's + # box and outputs it. If you change the 180 to 0, the player's box will be black and you + # won't be able to see it (because it will match the black background). + outputs.borders << [state.x, + state.y, + state.tile_size, + state.tile_size, 0, 180, 0] + end + + # Calls methods needed to perform calculations. + def calc + calc_world_lookup + calc_player + end + + # Performs calculations on world_lookup and sets values. + def calc_world_lookup + + # If the tile size isn't equal to the previous tile size, + # the previous tile size is set to the tile size, + # and world_lookup hash is set to empty. + if state.tile_size != state.previous_tile_size + state.previous_tile_size = state.tile_size + state.world_lookup = {} # empty hash + end + + # return if the world_lookup hash has keys (or, in other words, is not empty) + # return unless the world collection has values inside of it (or is not empty) + return if state.world_lookup.keys.length > 0 + return unless state.world.length > 0 + + # Starts with an empty hash for world_lookup. + # Searches through the world and finds the coordinates that exist. + state.world_lookup = {} + state.world.each { |x, y| state.world_lookup[[x, y]] = true } + + # Assigns world_collision_rects for every sprite drawn. + state.world_collision_rects = + state.world_lookup + .keys + .map do |coord_x, coord_y| + s = state.tile_size + # multiply by tile size so the grid coordinates; sets pixel value + # don't forget that position is denoted by bottom left corner + # set x = coord_x or y = coord_y and see what happens! + x = s * coord_x + y = s * coord_y + { + # The values added to x, y, and s position the world_collision_rects so they all appear + # stacked (on top of world rects) but don't directly overlap. + # Remove these added values and mess around with the rect placement! + args: [coord_x, coord_y], + left_right: [x, y + 4, s, s - 6], # hash keys and values + top: [x + 4, y + 6, s - 8, s - 6], + bottom: [x + 1, y - 1, s - 2, s - 8], + } + end + end + + # Performs calculations to change the x and y values of the player's box. + def calc_player + + # Since acceleration is the change in velocity, the change in y (dy) increases every frame. + # What goes up must come down because of gravity. + state.dy += state.gravity + + # Calls the calc_box_collision and calc_edge_collision methods. + calc_box_collision + calc_edge_collision + + # Since velocity is the change in position, the change in y increases by dy. Same with x and dx. + state.y += state.dy + state.x += state.dx + + # Scales dx down. + state.dx *= 0.8 + end + + # Calls methods needed to determine collisions between player and world_collision rects. + def calc_box_collision + return unless state.world_lookup.keys.length > 0 # return unless hash has atleast 1 key + collision_floor! + collision_left! + collision_right! + collision_ceiling! + end + + # Finds collisions between the bottom of the player's rect and the top of a world_collision_rect. + def collision_floor! + return unless state.dy <= 0 # return unless player is going down or is as far down as possible + player_rect = [state.x, state.y - 0.1, state.tile_size, state.tile_size] # definition of player + + # Goes through world_collision_rects to find all intersections between the bottom of player's rect and + # the top of a world_collision_rect (hence the "-0.1" above) + floor_collisions = state.world_collision_rects + .find_all { |r| r[:top].intersect_rect?(player_rect, collision_tollerance) } + .first + + return unless floor_collisions # return unless collision occurred + state.y = floor_collisions[:top].top # player's y is set to the y of the top of the collided rect + state.dy = 0 # if a collision occurred, the player's rect isn't moving because its path is blocked + end + + # Finds collisions between the player's left side and the right side of a world_collision_rect. + def collision_left! + return unless state.dx < 0 # return unless player is moving left + player_rect = [state.x - 0.1, state.y, state.tile_size, state.tile_size] + + # Goes through world_collision_rects to find all intersections beween the player's left side and the + # right side of a world_collision_rect. + left_side_collisions = state.world_collision_rects + .find_all { |r| r[:left_right].intersect_rect?(player_rect, collision_tollerance) } + .first + + return unless left_side_collisions # return unless collision occurred + + # player's x is set to the value of the x of the collided rect's right side + state.x = left_side_collisions[:left_right].right + state.dx = 0 # player isn't moving left because its path is blocked + end + + # Finds collisions between the right side of the player and the left side of a world_collision_rect. + def collision_right! + return unless state.dx > 0 # return unless player is moving right + player_rect = [state.x + 0.1, state.y, state.tile_size, state.tile_size] + + # Goes through world_collision_rects to find all intersections between the player's right side + # and the left side of a world_collision_rect (hence the "+0.1" above) + right_side_collisions = state.world_collision_rects + .find_all { |r| r[:left_right].intersect_rect?(player_rect, collision_tollerance) } + .first + + return unless right_side_collisions # return unless collision occurred + + # player's x is set to the value of the collided rect's left, minus the size of a rect + # tile size is subtracted because player's position is denoted by bottom left corner + state.x = right_side_collisions[:left_right].left - state.tile_size + state.dx = 0 # player isn't moving right because its path is blocked + end + + # Finds collisions between the top of the player's rect and the bottom of a world_collision_rect. + def collision_ceiling! + return unless state.dy > 0 # return unless player is moving up + player_rect = [state.x, state.y + 0.1, state.tile_size, state.tile_size] + + # Goes through world_collision_rects to find intersections between the bottom of a + # world_collision_rect and the top of the player's rect (hence the "+0.1" above) + ceil_collisions = state.world_collision_rects + .find_all { |r| r[:bottom].intersect_rect?(player_rect, collision_tollerance) } + .first + + return unless ceil_collisions # return unless collision occurred + + # player's y is set to the bottom y of the rect it collided with, minus the size of a rect + state.y = ceil_collisions[:bottom].y - state.tile_size + state.dy = 0 # if a collision occurred, the player isn't moving up because its path is blocked + end + + # Makes sure the player remains within the screen's dimensions. + def calc_edge_collision + + #Ensures that the player doesn't fall below the map. + if state.y < 0 + state.y = 0 + state.dy = 0 + + #Ensures that the player doesn't go too high. + # Position of player is denoted by bottom left hand corner, which is why we have to subtract the + # size of the player's box (so it remains visible on the screen) + elsif state.y > 720 - state.tile_size # if the player's y position exceeds the height of screen + state.y = 720 - state.tile_size # the player will remain as high as possible while staying on screen + state.dy = 0 + end + + # Ensures that the player remains in the horizontal range that it is supposed to. + if state.x >= 1280 - state.tile_size && state.dx > 0 # if player moves too far right + state.x = 1280 - state.tile_size # player will remain as right as possible while staying on screen + state.dx = 0 + elsif state.x <= 0 && state.dx < 0 # if player moves too far left + state.x = 0 # player will remain as left as possible while remaining on screen + state.dx = 0 + end + end + + # Processes input from the user on the keyboard. + def process_inputs + if inputs.mouse.down + state.world_lookup = {} + x, y = to_coord inputs.mouse.down.point # gets x, y coordinates for the grid + + if state.world.any? { |loc| loc == [x, y] } # checks if coordinates duplicate + state.world = state.world.reject { |loc| loc == [x, y] } # erases tile space + else + state.world << [x, y] # If no duplicates, adds to world collection + end + end + + # Sets dx to 0 if the player lets go of arrow keys. + if inputs.keyboard.key_up.right + state.dx = 0 + elsif inputs.keyboard.key_up.left + state.dx = 0 + end + + # Sets dx to 3 in whatever direction the player chooses. + if inputs.keyboard.key_held.right # if right key is pressed + state.dx = 3 + elsif inputs.keyboard.key_held.left # if left key is pressed + state.dx = -3 + end + + #Sets dy to 5 to make the player ~fly~ when they press the space bar + if inputs.keyboard.key_held.space + state.dy = 5 + end + end + + def to_coord point + + # Integer divides (idiv) point.x to turn into grid + # Then, you can just multiply each integer by state.tile_size later so the grid coordinates. + [point.x.idiv(state.tile_size), point.y.idiv(state.tile_size)] + end + + # Represents the tolerance for a collision between the player's rect and another rect. + def collision_tollerance + 0.0 + end +end + +$platformer_physics = PoorManPlatformerPhysics.new + +def tick args + $platformer_physics.grid = args.grid + $platformer_physics.inputs = args.inputs + $platformer_physics.state = args.state + $platformer_physics.outputs = args.outputs + $platformer_physics.tick + tick_instructions args, "Sample app shows platformer collisions. CLICK to place box. ARROW keys to move around. SPACE to jump." +end + +def tick_instructions args, text, y = 715 + return if args.state.key_event_occurred + if args.inputs.mouse.click || + args.inputs.keyboard.directional_vector || + args.inputs.keyboard.key_down.enter || + args.inputs.keyboard.key_down.escape + args.state.key_event_occurred = true + end + + args.outputs.debug << [0, y - 50, 1280, 60].solid + args.outputs.debug << [640, y, text, 1, 1, 255, 255, 255].label + args.outputs.debug << [640, y - 25, "(click to dismiss instructions)" , -2, 1, 255, 255, 255].label +end diff --git a/samples/04_physics_and_collisions/04_box_collision/license-for-sample.txt b/samples/04_physics_and_collisions/04_box_collision/license-for-sample.txt new file mode 100644 index 0000000..100dcec --- /dev/null +++ b/samples/04_physics_and_collisions/04_box_collision/license-for-sample.txt @@ -0,0 +1,9 @@ +Copyright 2019 DragonRuby LLC + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/samples/04_physics_and_collisions/04_box_collision/replay.txt b/samples/04_physics_and_collisions/04_box_collision/replay.txt new file mode 100644 index 0000000..c7ea2cb --- /dev/null +++ b/samples/04_physics_and_collisions/04_box_collision/replay.txt @@ -0,0 +1,218 @@ +replay_version 2.0 +stopped_at 982 +seed 100 +recorded_at Mon Sep 30 00:11:39 2019 +[:mouse_move, 524, 240, 2, 1, 116] +[:mouse_move, 524, 246, 2, 2, 117] +[:mouse_move, 522, 258, 2, 3, 118] +[:mouse_move, 521, 270, 2, 4, 119] +[:mouse_move, 518, 284, 2, 5, 120] +[:mouse_move, 516, 291, 2, 6, 121] +[:mouse_move, 507, 317, 2, 7, 122] +[:mouse_move, 500, 331, 2, 8, 123] +[:mouse_move, 488, 357, 2, 9, 124] +[:mouse_move, 465, 395, 2, 10, 125] +[:mouse_move, 452, 416, 2, 11, 126] +[:mouse_move, 429, 451, 2, 12, 127] +[:mouse_move, 400, 485, 2, 13, 128] +[:mouse_move, 375, 509, 2, 14, 129] +[:mouse_move, 356, 522, 2, 15, 130] +[:mouse_move, 324, 543, 2, 16, 131] +[:mouse_move, 318, 546, 2, 17, 132] +[:mouse_move, 283, 562, 2, 18, 133] +[:mouse_move, 273, 564, 2, 19, 134] +[:mouse_move, 259, 566, 2, 20, 135] +[:mouse_move, 256, 567, 2, 21, 136] +[:mouse_move, 252, 567, 2, 22, 137] +[:mouse_move, 246, 567, 2, 23, 138] +[:mouse_move, 244, 567, 2, 24, 139] +[:mouse_move, 240, 566, 2, 25, 140] +[:mouse_move, 238, 561, 2, 26, 141] +[:mouse_move, 234, 555, 2, 27, 142] +[:mouse_move, 230, 550, 2, 28, 143] +[:mouse_move, 228, 547, 2, 29, 144] +[:mouse_move, 223, 542, 2, 30, 145] +[:mouse_move, 220, 540, 2, 31, 146] +[:mouse_move, 215, 537, 2, 32, 147] +[:mouse_move, 212, 535, 2, 33, 148] +[:mouse_move, 210, 532, 2, 34, 149] +[:mouse_move, 207, 528, 2, 35, 150] +[:mouse_move, 206, 526, 2, 36, 151] +[:mouse_move, 204, 523, 2, 37, 152] +[:mouse_move, 204, 522, 2, 38, 153] +[:mouse_move, 204, 521, 2, 39, 154] +[:mouse_button_pressed, 1, 0, 1, 40, 166] +[:mouse_button_up, 1, 0, 1, 41, 173] +[:mouse_move, 209, 521, 2, 42, 179] +[:mouse_move, 215, 521, 2, 43, 180] +[:mouse_move, 244, 521, 2, 44, 181] +[:mouse_move, 261, 521, 2, 45, 182] +[:mouse_move, 305, 521, 2, 46, 183] +[:mouse_move, 330, 519, 2, 47, 184] +[:mouse_move, 375, 511, 2, 48, 185] +[:mouse_move, 398, 505, 2, 49, 186] +[:mouse_move, 431, 493, 2, 50, 187] +[:mouse_move, 447, 486, 2, 51, 188] +[:mouse_move, 465, 476, 2, 52, 189] +[:mouse_move, 475, 470, 2, 53, 190] +[:mouse_move, 489, 461, 2, 54, 191] +[:mouse_move, 490, 458, 2, 55, 192] +[:mouse_move, 494, 455, 2, 56, 193] +[:mouse_move, 498, 450, 2, 57, 194] +[:mouse_move, 499, 448, 2, 58, 195] +[:mouse_move, 501, 445, 2, 59, 196] +[:mouse_move, 502, 444, 2, 60, 198] +[:mouse_button_pressed, 1, 0, 1, 61, 200] +[:mouse_button_up, 1, 0, 1, 62, 208] +[:mouse_move, 503, 443, 2, 63, 212] +[:mouse_move, 507, 445, 2, 64, 213] +[:mouse_move, 516, 453, 2, 65, 214] +[:mouse_move, 523, 461, 2, 66, 215] +[:mouse_move, 546, 493, 2, 67, 216] +[:mouse_move, 556, 509, 2, 68, 217] +[:mouse_move, 578, 546, 2, 69, 218] +[:mouse_move, 582, 555, 2, 70, 219] +[:mouse_move, 593, 578, 2, 71, 220] +[:mouse_move, 595, 582, 2, 72, 221] +[:mouse_move, 598, 591, 2, 73, 222] +[:mouse_move, 606, 611, 2, 74, 223] +[:mouse_move, 607, 617, 2, 75, 224] +[:mouse_move, 607, 627, 2, 76, 225] +[:mouse_move, 607, 632, 2, 77, 226] +[:mouse_move, 607, 638, 2, 78, 227] +[:mouse_move, 607, 640, 2, 79, 228] +[:mouse_move, 606, 643, 2, 80, 229] +[:mouse_move, 606, 644, 2, 81, 230] +[:mouse_button_pressed, 1, 0, 1, 82, 231] +[:mouse_move, 606, 645, 2, 83, 231] +[:mouse_button_up, 1, 0, 1, 84, 239] +[:mouse_move, 606, 643, 2, 85, 240] +[:mouse_move, 600, 636, 2, 86, 241] +[:mouse_move, 594, 631, 2, 87, 242] +[:mouse_move, 566, 613, 2, 88, 243] +[:mouse_move, 548, 607, 2, 89, 244] +[:mouse_move, 526, 599, 2, 90, 245] +[:mouse_move, 465, 572, 2, 91, 246] +[:mouse_move, 399, 533, 2, 92, 247] +[:mouse_move, 370, 512, 2, 93, 248] +[:mouse_move, 345, 489, 2, 94, 249] +[:mouse_move, 320, 459, 2, 95, 250] +[:mouse_move, 306, 439, 2, 96, 251] +[:mouse_move, 290, 403, 2, 97, 252] +[:mouse_move, 282, 368, 2, 98, 253] +[:mouse_move, 279, 351, 2, 99, 254] +[:mouse_move, 279, 332, 2, 100, 255] +[:mouse_move, 279, 308, 2, 101, 256] +[:mouse_move, 279, 303, 2, 102, 257] +[:mouse_move, 280, 287, 2, 103, 258] +[:mouse_move, 280, 282, 2, 104, 259] +[:mouse_move, 280, 278, 2, 105, 260] +[:mouse_move, 280, 276, 2, 106, 261] +[:mouse_button_pressed, 1, 0, 1, 107, 262] +[:mouse_move, 280, 275, 2, 108, 262] +[:mouse_button_up, 1, 0, 1, 109, 269] +[:key_down_raw, 1073741903, 0, 2, 110, 324] +[:key_down_raw, 1073741903, 0, 2, 111, 349] +[:key_down_raw, 1073741903, 0, 2, 112, 351] +[:key_down_raw, 1073741903, 0, 2, 113, 353] +[:key_down_raw, 1073741903, 0, 2, 114, 355] +[:key_down_raw, 1073741903, 0, 2, 115, 357] +[:key_down_raw, 1073741903, 0, 2, 116, 359] +[:key_down_raw, 1073741903, 0, 2, 117, 361] +[:key_down_raw, 1073741903, 0, 2, 118, 363] +[:key_down_raw, 1073741903, 0, 2, 119, 365] +[:key_down_raw, 1073741903, 0, 2, 120, 367] +[:key_down_raw, 1073741903, 0, 2, 121, 369] +[:key_down_raw, 8, 0, 2, 122, 371] +[:key_up_raw, 1073741903, 0, 2, 123, 382] +[:key_down_raw, 8, 0, 2, 124, 396] +[:key_up_raw, 8, 0, 2, 125, 396] +[:key_down_raw, 1073741903, 0, 2, 126, 400] +[:key_down_raw, 32, 0, 2, 127, 413] +[:key_up_raw, 1073741903, 0, 2, 128, 419] +[:key_down_raw, 1073741904, 0, 2, 129, 421] +[:key_down_raw, 1073741903, 0, 2, 130, 443] +[:key_up_raw, 1073741904, 0, 2, 131, 443] +[:key_down_raw, 1073741903, 0, 2, 132, 468] +[:key_up_raw, 1073741903, 0, 2, 133, 468] +[:key_down_raw, 1073741904, 0, 2, 134, 468] +[:key_down_raw, 1073741904, 0, 2, 135, 494] +[:key_down_raw, 1073741904, 0, 2, 136, 495] +[:key_down_raw, 1073741904, 0, 2, 137, 498] +[:key_down_raw, 1073741904, 0, 2, 138, 499] +[:key_down_raw, 1073741904, 0, 2, 139, 502] +[:key_down_raw, 1073741904, 0, 2, 140, 504] +[:key_down_raw, 1073741904, 0, 2, 141, 506] +[:key_down_raw, 1073741904, 0, 2, 142, 508] +[:key_down_raw, 1073741904, 0, 2, 143, 509] +[:key_down_raw, 1073741904, 0, 2, 144, 512] +[:key_down_raw, 1073741903, 0, 2, 145, 512] +[:key_up_raw, 1073741904, 0, 2, 146, 528] +[:key_down_raw, 1073741903, 0, 2, 147, 537] +[:key_down_raw, 1073741903, 0, 2, 148, 539] +[:key_down_raw, 1073741903, 0, 2, 149, 541] +[:key_down_raw, 1073741903, 0, 2, 150, 543] +[:key_down_raw, 1073741903, 0, 2, 151, 545] +[:key_down_raw, 1073741903, 0, 2, 152, 547] +[:key_down_raw, 1073741903, 0, 2, 153, 549] +[:key_down_raw, 1073741903, 0, 2, 154, 551] +[:key_down_raw, 1073741903, 0, 2, 155, 553] +[:key_down_raw, 1073741903, 0, 2, 156, 555] +[:key_down_raw, 1073741903, 0, 2, 157, 557] +[:key_down_raw, 1073741903, 0, 2, 158, 559] +[:key_down_raw, 1073741903, 0, 2, 159, 561] +[:key_down_raw, 1073741903, 0, 2, 160, 563] +[:key_down_raw, 1073741903, 0, 2, 161, 565] +[:key_down_raw, 1073741903, 0, 2, 162, 567] +[:key_down_raw, 1073741903, 0, 2, 163, 569] +[:key_down_raw, 1073741903, 0, 2, 164, 572] +[:key_down_raw, 1073741903, 0, 2, 165, 573] +[:key_down_raw, 1073741903, 0, 2, 166, 576] +[:key_down_raw, 1073741903, 0, 2, 167, 577] +[:key_down_raw, 1073741903, 0, 2, 168, 579] +[:key_down_raw, 1073741903, 0, 2, 169, 581] +[:key_down_raw, 1073741903, 0, 2, 170, 583] +[:key_down_raw, 1073741903, 0, 2, 171, 586] +[:key_down_raw, 1073741903, 0, 2, 172, 588] +[:key_down_raw, 1073741903, 0, 2, 173, 590] +[:key_down_raw, 1073741903, 0, 2, 174, 592] +[:key_down_raw, 1073741903, 0, 2, 175, 594] +[:key_up_raw, 32, 0, 2, 176, 594] +[:key_down_raw, 1073741903, 0, 2, 177, 596] +[:key_up_raw, 1073741903, 0, 2, 178, 597] +[:key_down_raw, 1073741904, 0, 2, 179, 670] +[:key_down_raw, 1073741904, 0, 2, 180, 695] +[:key_down_raw, 1073741904, 0, 2, 181, 697] +[:key_down_raw, 1073741904, 0, 2, 182, 699] +[:key_down_raw, 1073741904, 0, 2, 183, 701] +[:key_down_raw, 1073741904, 0, 2, 184, 703] +[:key_down_raw, 1073741904, 0, 2, 185, 705] +[:key_down_raw, 1073741904, 0, 2, 186, 707] +[:key_up_raw, 1073741904, 0, 2, 187, 708] +[:key_down_raw, 1073741903, 0, 2, 188, 722] +[:key_up_raw, 1073741903, 0, 2, 189, 733] +[:key_down_raw, 1073741903, 0, 2, 190, 762] +[:key_down_raw, 32, 0, 2, 191, 786] +[:key_down_raw, 32, 0, 2, 192, 811] +[:key_down_raw, 32, 0, 2, 193, 813] +[:key_down_raw, 32, 0, 2, 194, 815] +[:key_down_raw, 32, 0, 2, 195, 817] +[:key_down_raw, 32, 0, 2, 196, 819] +[:key_down_raw, 32, 0, 2, 197, 821] +[:key_down_raw, 32, 0, 2, 198, 823] +[:key_down_raw, 32, 0, 2, 199, 825] +[:key_down_raw, 32, 0, 2, 200, 827] +[:key_down_raw, 32, 0, 2, 201, 829] +[:key_down_raw, 32, 0, 2, 202, 831] +[:key_down_raw, 32, 0, 2, 203, 833] +[:key_down_raw, 32, 0, 2, 204, 835] +[:key_down_raw, 32, 0, 2, 205, 837] +[:key_down_raw, 32, 0, 2, 206, 839] +[:key_down_raw, 32, 0, 2, 207, 841] +[:key_up_raw, 32, 0, 2, 208, 843] +[:key_up_raw, 1073741903, 0, 2, 209, 848] +[:key_down_raw, 1073741903, 0, 2, 210, 871] +[:key_up_raw, 1073741903, 0, 2, 211, 877] +[:key_down_raw, 1073742051, 1024, 2, 212, 981] +[:key_down_raw, 113, 1024, 2, 213, 981] +[:key_up_raw, 113, 1024, 2, 214, 981] |
