aboutsummaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/game.gd51
-rw-r--r--client/game.tscn10
-rw-r--r--client/global.gd4
-rw-r--r--client/map/item_factory.gd4
-rw-r--r--client/map/map.gd3
-rw-r--r--client/map/tiles/chair.gd2
-rw-r--r--client/map/tiles/counter.gd4
-rw-r--r--client/map/tiles/door.gd2
-rw-r--r--client/map/tiles/floor.gd6
-rw-r--r--client/map/tiles/wall_tile.gd2
-rw-r--r--client/menu/ingame_menu.gd9
-rw-r--r--client/menu/ingame_menu.tscn112
-rw-r--r--client/menu/main_menu.gd5
-rw-r--r--client/menu/scene_transition.gd24
-rw-r--r--client/menu/scene_transition.tscn9
-rw-r--r--client/multiplayer.gd14
-rw-r--r--client/multiplayer.tscn6
-rw-r--r--client/player/controllable_player.gd10
-rw-r--r--client/project.godot7
19 files changed, 228 insertions, 56 deletions
diff --git a/client/game.gd b/client/game.gd
index f1f4e065..67b92679 100644
--- a/client/game.gd
+++ b/client/game.gd
@@ -1,38 +1,39 @@
# Undercooked - a game about cooking
# Copyright 2024 nokoe
# Copyright 2024 metamuffin
-#
+#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License only.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
-#
+#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
-#
+#
class_name Game
extends Node3D
@onready var camera: FollowCamera = $FollowCamera
@onready var map: Map = $Map
+@onready var mp: Multiplayer = $Multiplayer
@onready var marker: Marker = $Marker
var marker_target = Vector3(0,0,0)
var players := {}
# Called when the node enters the scene tree for the first time.
func _ready():
- Multiplayer.connect_client()
- await Multiplayer.init
- if Multiplayer.player_id == -1:
+ await mp.init
+ if mp.player_id == -1:
push_error("multiplayer has not been initialized")
- Multiplayer.connect("add_player",
+
+ mp.connect("add_player",
func(player: int, player_name: String, pos: Vector2, character: int):
var player_instance: Player
- if player == Multiplayer.player_id:
+ if player == mp.player_id:
player_instance = ControllablePlayer.new(player, player_name, pos, character, self)
camera.target = player_instance
else:
@@ -41,13 +42,15 @@ func _ready():
add_child(player_instance)
)
- Multiplayer.connect("position",
+ mp.connect("update_map", map.update)
+
+ mp.connect("position",
func(player: int, pos: Vector2, rot: float):
var player_instance: Player = players[player]
player_instance.update_position(pos, rot)
)
- Multiplayer.connect("remove_player", func(id: int):
+ mp.connect("remove_player", func(id: int):
var player: Player = players.get(id)
if player != null:
if player.hand != null:
@@ -56,55 +59,55 @@ func _ready():
player.queue_free()
)
- Multiplayer.connect("set_tile_item", func(tile: Vector2i, item: int):
+ mp.connect("set_tile_item", func(tile: Vector2i, item: int):
var t: Floor = map.tile_by_pos[str(tile)]
- var i = ItemFactory.produce(item, t.item_base)
+ var i = ItemFactory.produce(mp.item_names[item], t.item_base)
add_child(i)
- i.name = Multiplayer.item_names[item]
+ i.name = mp.item_names[item]
t.set_item(i)
)
- Multiplayer.connect("remove_tile_item", func(tile: Vector2i):
+ mp.connect("remove_tile_item", func(tile: Vector2i):
var t: Floor = map.tile_by_pos[str(tile)]
t.take_item().queue_free()
)
- Multiplayer.connect("set_player_item", func(player: int, item: int):
+ mp.connect("set_player_item", func(player: int, item: int):
var p: Player = players[player]
- var i = ItemFactory.produce(item, p.hand_base)
+ var i = ItemFactory.produce(mp.item_names[item], p.hand_base)
add_child(i)
- i.name = Multiplayer.item_names[item]
+ i.name = mp.item_names[item]
p.set_item(i)
)
- Multiplayer.connect("remove_player_item", func(player: int):
+ mp.connect("remove_player_item", func(player: int):
var p: Player = players[player]
p.remove_item().queue_free()
)
- Multiplayer.connect("take_item", func(tile: Vector2i, player: int):
+ mp.connect("take_item", func(tile: Vector2i, player: int):
var t: Floor = map.tile_by_pos[str(tile)]
var p: Player = players[player]
p.take_item(t)
)
- Multiplayer.connect("put_item", func(tile: Vector2i, player: int):
+ mp.connect("put_item", func(tile: Vector2i, player: int):
var t: FullTile = map.tile_by_pos[str(tile)]
var p: Player = players[player]
p.put_item(t)
)
- Multiplayer.connect("set_progress", func(tile: Vector2i, progress: float, warn: bool):
+ mp.connect("set_progress", func(tile: Vector2i, progress: float, warn: bool):
var t: FullTile = map.tile_by_pos[str(tile)]
t.progress(progress, warn)
)
- Multiplayer.connect("set_finished", func(tile: Vector2i, warn: bool):
+ mp.connect("set_finished", func(tile: Vector2i, warn: bool):
var t: FullTile = map.tile_by_pos[str(tile)]
t.finish(warn)
)
- Multiplayer.send_join("Blub", 1)
+ mp.send_join("Blub", 1)
func _process(delta):
diff --git a/client/game.tscn b/client/game.tscn
index 34c2fe40..a7485c1d 100644
--- a/client/game.tscn
+++ b/client/game.tscn
@@ -1,9 +1,11 @@
-[gd_scene load_steps=8 format=3 uid="uid://c6krh36hoqfg8"]
+[gd_scene load_steps=10 format=3 uid="uid://c6krh36hoqfg8"]
[ext_resource type="PackedScene" uid="uid://cs8gxa22c6joh" path="res://map/map.tscn" id="1_ex12v"]
[ext_resource type="Script" path="res://game.gd" id="1_sftfn"]
[ext_resource type="PackedScene" uid="uid://b31mlnao6ybt8" path="res://player/follow_camera.tscn" id="2_s8y6o"]
[ext_resource type="PackedScene" uid="uid://c0euiv7duqfp4" path="res://player/marker.tscn" id="4_igl0o"]
+[ext_resource type="PackedScene" uid="uid://bg2d78ycorcqk" path="res://menu/scene_transition.tscn" id="5_yg6cl"]
+[ext_resource type="Script" path="res://multiplayer.gd" id="6_fbxu8"]
[sub_resource type="PhysicalSkyMaterial" id="PhysicalSkyMaterial_mvn2w"]
@@ -17,6 +19,9 @@ sky = SubResource("Sky_ultpf")
[node name="Game" type="Node3D"]
script = ExtResource("1_sftfn")
+[node name="Multiplayer" type="Node" parent="."]
+script = ExtResource("6_fbxu8")
+
[node name="Map" parent="." instance=ExtResource("1_ex12v")]
[node name="FollowCamera" parent="." instance=ExtResource("2_s8y6o")]
@@ -30,3 +35,6 @@ transform = Transform3D(1, 0, 0, 0, 0.258819, 0.965926, 0, -0.965926, 0.258819,
[node name="Marker" parent="." instance=ExtResource("4_igl0o")]
visible = false
+
+[node name="SceneTransition" parent="." instance=ExtResource("5_yg6cl")]
+ingame = true
diff --git a/client/global.gd b/client/global.gd
new file mode 100644
index 00000000..c0f9cc57
--- /dev/null
+++ b/client/global.gd
@@ -0,0 +1,4 @@
+extends Node
+
+var server_url = ""
+var character = 1
diff --git a/client/map/item_factory.gd b/client/map/item_factory.gd
index f66f990d..1fa21b27 100644
--- a/client/map/item_factory.gd
+++ b/client/map/item_factory.gd
@@ -16,8 +16,8 @@
class_name ItemFactory
extends Object
-static func produce(idx: int, owned_by: Node3D) -> Item:
- match Multiplayer.item_names[idx]:
+static func produce(name: String, owned_by: Node3D) -> Item:
+ match name:
"plate":
return Plate.new(owned_by)
"dirty-plate":
diff --git a/client/map/map.gd b/client/map/map.gd
index 913c29da..a26da0fd 100644
--- a/client/map/map.gd
+++ b/client/map/map.gd
@@ -20,9 +20,6 @@ extends Node3D
var tile_by_pos: Dictionary = {}
-func _ready():
- Multiplayer.connect("update_map", update)
-
func update(pos, tile_name, neighbors):
var instance: Floor
var node_name = str(pos)
diff --git a/client/map/tiles/chair.gd b/client/map/tiles/chair.gd
index a821ac80..b7014028 100644
--- a/client/map/tiles/chair.gd
+++ b/client/map/tiles/chair.gd
@@ -21,7 +21,7 @@ func _init(rename: String, neighbors: Array):
var chair = load("res://map/tiles/chair.tscn").instantiate()
var facing = 0;
for i in range(4):
- if tile_name(neighbors[i]) == "table":
+ if neighbors[i] == "table":
facing = i
break
base.add_child(chair)
diff --git a/client/map/tiles/counter.gd b/client/map/tiles/counter.gd
index 6783d486..62868087 100644
--- a/client/map/tiles/counter.gd
+++ b/client/map/tiles/counter.gd
@@ -43,7 +43,7 @@ func _init(rename: String, neighbors: Array):
for start in range(4):
var series = 0
for i in range(4):
- if tile_name(neighbors[(start + i) % 4]) == "floor":
+ if neighbors[(start + i) % 4] == "floor":
series += 1
else:
break
@@ -55,7 +55,7 @@ func _init(rename: String, neighbors: Array):
# backsplash
facing = max_idx
if max_series == 1:
- if WallTile.is_wall(tile_name(neighbors[(max_idx + 2) % 4])):
+ if WallTile.is_wall(neighbors[(max_idx + 2) % 4]):
kind = CounterKind.STRAIGHT_BACKSPLASH
else:
kind = CounterKind.STRAIGHT
diff --git a/client/map/tiles/door.gd b/client/map/tiles/door.gd
index 79f6ee85..5d86c000 100644
--- a/client/map/tiles/door.gd
+++ b/client/map/tiles/door.gd
@@ -21,7 +21,7 @@ func _init(rename: String, neighbors: Array):
var facing = 0
for i in range(4):
- if tile_name(neighbors[i]) == "door":
+ if neighbors[i] == "door":
facing = i % 4
base.add_child(load("res://map/tiles/door.tscn").instantiate())
turn_facing(facing)
diff --git a/client/map/tiles/floor.gd b/client/map/tiles/floor.gd
index 2433849d..c5089feb 100644
--- a/client/map/tiles/floor.gd
+++ b/client/map/tiles/floor.gd
@@ -46,12 +46,6 @@ func _init(rename: String, _neighbors: Array):
func turn_facing(facing: Facing):
base.rotate_y(facing * 0.5 * PI + PI)
-func tile_name(idx):
- if idx == null:
- return null
- return Multiplayer.tile_names[idx]
-
-
# defines where items go when interacting
static func interact_target() -> Vector3:
return Vector3(0, 0, 0)
diff --git a/client/map/tiles/wall_tile.gd b/client/map/tiles/wall_tile.gd
index 85e3e6ff..08d3c080 100644
--- a/client/map/tiles/wall_tile.gd
+++ b/client/map/tiles/wall_tile.gd
@@ -40,7 +40,7 @@ func _init(rename: String, neighbors: Array):
for start in range(4):
var series = 0
for i in range(4):
- var i_name = tile_name(neighbors[(start + i) % 4])
+ var i_name = neighbors[(start + i) % 4]
if is_wall(i_name):
series += 1
else:
diff --git a/client/menu/ingame_menu.gd b/client/menu/ingame_menu.gd
new file mode 100644
index 00000000..7a6d8e1a
--- /dev/null
+++ b/client/menu/ingame_menu.gd
@@ -0,0 +1,9 @@
+extends Control
+
+@onready var anim = $AnimationPlayer
+
+func _on_main_menu_pressed():
+ get_parent().transition_to("res://menu/main_menu.tscn")
+
+func _on_quit_pressed():
+ get_parent().quit()
diff --git a/client/menu/ingame_menu.tscn b/client/menu/ingame_menu.tscn
new file mode 100644
index 00000000..7f2bfa5d
--- /dev/null
+++ b/client/menu/ingame_menu.tscn
@@ -0,0 +1,112 @@
+[gd_scene load_steps=10 format=3 uid="uid://lxlgtjm8hw7v"]
+
+[ext_resource type="Script" path="res://menu/ingame_menu.gd" id="1_gd1i3"]
+[ext_resource type="Shader" path="res://menu/blur_mix.gdshader" id="1_o42mc"]
+[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://menu/theme.tres" id="1_tm331"]
+[ext_resource type="FontFile" uid="uid://bo4vh5xkpvrh1" path="res://menu/font-sansita-swashed.woff2" id="2_4u0ox"]
+
+[sub_resource type="Animation" id="Animation_8sedy"]
+length = 0.001
+tracks/0/type = "bezier"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("side:position:x")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"handle_modes": PackedInt32Array(0),
+"points": PackedFloat32Array(-400, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0)
+}
+
+[sub_resource type="Animation" id="Animation_660jl"]
+resource_name = "activate"
+tracks/0/type = "bezier"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("side:position:x")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"handle_modes": PackedInt32Array(0, 0),
+"points": PackedFloat32Array(-400, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0, 1)
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_u0kyp"]
+_data = {
+"RESET": SubResource("Animation_8sedy"),
+"activate": SubResource("Animation_660jl")
+}
+
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_o2vtr"]
+shader = ExtResource("1_o42mc")
+shader_parameter/blur_amount = 3.5
+shader_parameter/mix_amount = 0.3
+shader_parameter/color_over = null
+
+[sub_resource type="FontVariation" id="FontVariation_ud3l8"]
+base_font = ExtResource("2_4u0ox")
+variation_embolden = 0.5
+
+[node name="IngameMenu" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme = ExtResource("1_tm331")
+script = ExtResource("1_gd1i3")
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+libraries = {
+"": SubResource("AnimationLibrary_u0kyp")
+}
+speed_scale = 8.0
+
+[node name="side" type="PanelContainer" parent="."]
+material = SubResource("ShaderMaterial_o2vtr")
+layout_mode = 1
+anchors_preset = 9
+anchor_bottom = 1.0
+offset_left = -400.0
+offset_right = -90.0
+offset_bottom = 1944.0
+grow_vertical = 2
+
+[node name="margin" type="MarginContainer" parent="side"]
+layout_mode = 2
+theme_override_constants/margin_left = 20
+theme_override_constants/margin_top = 20
+theme_override_constants/margin_right = 20
+theme_override_constants/margin_bottom = 20
+
+[node name="options" type="VBoxContainer" parent="side/margin"]
+layout_mode = 2
+
+[node name="title" type="Label" parent="side/margin/options"]
+layout_mode = 2
+auto_translate = false
+theme_override_colors/font_outline_color = Color(0.566408, 0.208917, 0.266045, 1)
+theme_override_constants/outline_size = 10
+theme_override_fonts/font = SubResource("FontVariation_ud3l8")
+theme_override_font_sizes/font_size = 48
+text = "Undercooked"
+
+[node name="spacer" type="Control" parent="side/margin/options"]
+custom_minimum_size = Vector2(0, 10)
+layout_mode = 2
+
+[node name="main_menu" type="Button" parent="side/margin/options"]
+layout_mode = 2
+text = "Main Menu"
+alignment = 0
+
+[node name="quit" type="Button" parent="side/margin/options"]
+layout_mode = 2
+text = "Quit"
+alignment = 0
+
+[connection signal="pressed" from="side/margin/options/main_menu" to="." method="_on_main_menu_pressed"]
+[connection signal="pressed" from="side/margin/options/quit" to="." method="_on_quit_pressed"]
diff --git a/client/menu/main_menu.gd b/client/menu/main_menu.gd
index 1ac80408..b19d72a6 100644
--- a/client/menu/main_menu.gd
+++ b/client/menu/main_menu.gd
@@ -25,7 +25,7 @@ func _ready():
quit_button.hide()
func _on_quit_pressed():
- get_tree().quit()
+ $SceneTransition.quit()
func _on_credits_pressed():
$SceneTransition.transition_to("res://menu/credits_menu.tscn")
@@ -45,10 +45,9 @@ func _on_quick_connect_pressed():
func connect_to(url):
print("Connecting to %s" % url)
- Multiplayer.url = url
+ Global.server_url = url
$SceneTransition.transition_to("res://game.tscn")
-
func _on_change_character_pressed():
$SceneTransition.transition_to("res://menu/character_menu.tscn")
diff --git a/client/menu/scene_transition.gd b/client/menu/scene_transition.gd
index 8b972c1b..3ab7cdb6 100644
--- a/client/menu/scene_transition.gd
+++ b/client/menu/scene_transition.gd
@@ -17,12 +17,34 @@ class_name SceneTransition
extends ColorRect
@onready var anim = $animation
+@export var ingame = false
func _ready():
self.visible = true
anim.play("fade_in")
func transition_to(path: String):
+ await out()
+ get_tree().change_scene_to_file(path)
+
+func quit():
+ await out()
+ get_tree().quit()
+
+func out():
+ if menu.visible:
+ menu.anim.play_backwards("activate")
+ await menu.anim.animation_finished
anim.play("fade_out")
await anim.animation_finished
- get_tree().change_scene_to_file(path)
+
+@onready var menu = $IngameMenu
+func _process(_delta):
+ if ingame:
+ if not menu.visible and Input.is_action_just_pressed("pause"):
+ menu.visible = true
+ menu.anim.play("activate")
+ elif menu.visible and Input.is_action_just_pressed("pause"):
+ menu.anim.play_backwards("activate")
+ await menu.anim.animation_finished
+ menu.visible = false
diff --git a/client/menu/scene_transition.tscn b/client/menu/scene_transition.tscn
index df21d242..ddfd6238 100644
--- a/client/menu/scene_transition.tscn
+++ b/client/menu/scene_transition.tscn
@@ -1,6 +1,7 @@
-[gd_scene load_steps=6 format=3 uid="uid://bg2d78ycorcqk"]
+[gd_scene load_steps=7 format=3 uid="uid://bg2d78ycorcqk"]
[ext_resource type="Script" path="res://menu/scene_transition.gd" id="1_fpbwj"]
+[ext_resource type="PackedScene" uid="uid://lxlgtjm8hw7v" path="res://menu/ingame_menu.tscn" id="2_aqaj2"]
[sub_resource type="Animation" id="Animation_xgn2a"]
length = 0.001
@@ -69,3 +70,9 @@ libraries = {
"": SubResource("AnimationLibrary_pea72")
}
speed_scale = 4.0
+
+[node name="IngameMenu" parent="." instance=ExtResource("2_aqaj2")]
+visible = false
+layout_mode = 1
+offset_right = 2304.0
+offset_bottom = 1296.0
diff --git a/client/multiplayer.gd b/client/multiplayer.gd
index 7ccfd377..2d4b9533 100644
--- a/client/multiplayer.gd
+++ b/client/multiplayer.gd
@@ -15,6 +15,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
+class_name Multiplayer
extends Node
signal init(player_id: int)
@@ -46,10 +47,15 @@ var player_id = -1
var tileid_by_pos: Dictionary = {}
-var url = ""
-func connect_client():
- socket.connect_to_url(url)
+func _ready():
+ print("Multiplayer connect");
+ socket.connect_to_url(Global.server_url)
connected = true
+func _notification(what):
+ if what == NOTIFICATION_PREDELETE:
+ print("Multiplayer disconnect");
+ socket.close()
+ connected = false
func _process(_delta):
if connected:
@@ -135,7 +141,7 @@ func handle_packet(bytes: PackedByteArray):
var pos: Array = decoded["pos"]
var neighbors: Array = decoded["neighbors"]
tileid_by_pos[str(Vector2i(pos[0],pos[1]))] = tile
- emit_signal("update_map", pos, tile_names[tile], neighbors)
+ emit_signal("update_map", pos, tile_names[tile], neighbors.map(func (x): return tile_names[x] if x != null else null))
"communicate":
var player = decoded["player"]
var message = decoded.get("message")
diff --git a/client/multiplayer.tscn b/client/multiplayer.tscn
new file mode 100644
index 00000000..74202c1b
--- /dev/null
+++ b/client/multiplayer.tscn
@@ -0,0 +1,6 @@
+[gd_scene load_steps=2 format=3 uid="uid://b0sibwbndh7jp"]
+
+[ext_resource type="Script" path="res://multiplayer.gd" id="1_xinpa"]
+
+[node name="Multiplayer" type="Node"]
+script = ExtResource("1_xinpa")
diff --git a/client/player/controllable_player.gd b/client/player/controllable_player.gd
index 65fcf960..a85d6c84 100644
--- a/client/player/controllable_player.gd
+++ b/client/player/controllable_player.gd
@@ -32,7 +32,7 @@ func _ready():
add_child(timer)
timer.start()
timer.connect("timeout", func():
- Multiplayer.send_position(position_, rotation_)
+ game.mp.send_position(position_, rotation_)
)
func _process(delta):
@@ -66,7 +66,7 @@ func collide(dt: float):
for xo in range(-1,2):
for yo in range(-1,2):
var tile = Vector2i(xo, yo) + Vector2i(self.position_);
- if !Multiplayer.get_tile_collision(tile): continue
+ if !game.mp.get_tile_collision(tile): continue
tile = Vector2(tile)
var d = aabb_point_distance(tile, tile + Vector2.ONE, self.position_);
if d > PLAYER_SIZE: continue
@@ -99,13 +99,13 @@ func interact():
var tile_idx = str(target)
var t: Floor = game.map.tile_by_pos.get(tile_idx)
if t != null:
- game.marker.set_interactive(Multiplayer.get_tile_interactive(target))
+ game.marker.set_interactive(game.mp.get_tile_interactive(target))
game.marker.visible = true
game.marker_target = t.item_base.global_position
if Input.is_action_just_pressed("interact"):
- Multiplayer.send_interact(target, true)
+ game.mp.send_interact(target, true)
t.interact()
elif Input.is_action_just_released("interact"):
- Multiplayer.send_interact(target, false)
+ game.mp.send_interact(target, false)
else:
game.marker.visible = false
diff --git a/client/project.godot b/client/project.godot
index aa024f72..d05e56e7 100644
--- a/client/project.godot
+++ b/client/project.godot
@@ -17,7 +17,7 @@ config/icon="res://icon.svg"
[autoload]
-Multiplayer="*res://multiplayer.gd"
+Global="*res://global.gd"
[input]
@@ -66,6 +66,11 @@ interact={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null)
]
}
+pause={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"echo":false,"script":null)
+]
+}
[internationalization]