Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d609bdb21d | |||
| 87c06cfd04 | |||
| a2e2e0f18c | |||
| d328836ec5 | |||
| 78667ade3d | |||
| 95eb1ebd58 | |||
| 534b81e1ba | |||
| f1666b3085 | |||
| 8ffc7bb427 | |||
| b2594f2983 | |||
| 106a8611be | |||
| 564cf7c168 | |||
| 0ba9de1095 | |||
| 7878d8b764 | |||
| fcb5ba372f | |||
| d791ec24b3 | |||
| 2bc77aa297 | |||
| 3db3d07029 | |||
| 012fae3b3a | |||
| 728cc4452d | |||
| a0428d90d6 | |||
| 82c9a229da | |||
| 02e77f8bce | |||
| c1e4cc93a9 | |||
| e9a6d5ed5c | |||
| 532505c812 | |||
| 8df1181be3 | |||
| 8998fcc84f | |||
| e2c67b79a9 | |||
| dc7f20face | |||
| aa44311c1f | |||
| f2be1197d6 | |||
| c5c0f50aad | |||
| 7966e2073c | |||
| 9cf81ffe62 | |||
| f1af3e00cc | |||
| 3888476c41 | |||
| b5dac49ef0 | |||
| 53fb1c6958 | |||
| 6f399db783 | |||
| acbad4aadf | |||
| 030c1685c7 | |||
| 8a1d4aa1ed | |||
| 5b32ea57d6 | |||
| e6b58236d7 | |||
| da1c491a09 | |||
| c058c274ac | |||
| 0376dd6450 | |||
| 1f73e04865 | |||
| 263e06a944 | |||
| 02f112f168 | |||
| 60d603bcef | |||
| ecc7912529 | |||
| 2cb96155f2 | |||
| e967664b73 | |||
| 782e24e411 | |||
| ead41f16d5 | |||
| 2863a7fc7d | |||
| 35511f2d66 | |||
| fab669738d | |||
| a6e31e55ed | |||
| 2d966647d2 | |||
| 2217bf76d1 | |||
| e57b7829a9 | |||
| b046094f82 | |||
| 21b8c41a9f | |||
| 5dba5cdc2f | |||
| a17becae83 | |||
| b6b0f45956 | |||
| dadf9298cb | |||
| aeda4516af | |||
| 296f003e58 | |||
| 5bfe9da28a | |||
| ea32353769 | |||
| 95f74d10cc | |||
| 3221c26769 | |||
| e0c80fc420 | |||
| 10e4038fea | |||
| 6dfeab105e | |||
| 14a0f44069 | |||
| 30900fcdce | |||
| 0f22e17422 | |||
| 32de8897c6 | |||
| 084915b877 | |||
| 4a4eb43bf0 | |||
| c11d6f5f76 | |||
| 442e5f1201 | |||
| 7c58d2994c | |||
| 64ba8b4f80 | |||
| e7c695afdc | |||
| 5b111d060f | |||
| 75383806f2 | |||
| 1e12e05d0b | |||
| 888861840e | |||
| 7c33fbc255 | |||
| ff34f393cd | |||
| ae15500f76 | |||
| fc5fcd4299 | |||
| cc1d89eb28 | |||
| 57b990d76e | |||
| e27d943e56 | |||
| 5598a5ac52 | |||
| b95a7c2dcc | |||
| d35d2dc306 | |||
| 3f8017a030 | |||
| 49c766e6f4 | |||
| 7b8401fb78 | |||
| b9392fa7d1 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,7 +1,8 @@
|
||||
.godot/
|
||||
/android/
|
||||
.vscode/
|
||||
|
||||
/build/*
|
||||
/build/
|
||||
|
||||
!.gdignore
|
||||
|
||||
|
||||
BIN
assets/models/key.bin
(Stored with Git LFS)
Normal file
BIN
assets/models/key.bin
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/models/key.glb
(Stored with Git LFS)
BIN
assets/models/key.glb
(Stored with Git LFS)
Binary file not shown.
277
assets/models/key.gltf
Normal file
277
assets/models/key.gltf
Normal file
@ -0,0 +1,277 @@
|
||||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v4.5.47",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Collection",
|
||||
"nodes":[
|
||||
0,
|
||||
6
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"NubMesh",
|
||||
"translation":[
|
||||
0,
|
||||
0.25,
|
||||
0.42500001192092896
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"TopLeft",
|
||||
"translation":[
|
||||
-0.5,
|
||||
0,
|
||||
-0.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"BottomRight",
|
||||
"translation":[
|
||||
0.5,
|
||||
0,
|
||||
0.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"TopRight",
|
||||
"translation":[
|
||||
0.5,
|
||||
0,
|
||||
-0.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"BottomLeft",
|
||||
"translation":[
|
||||
-0.5,
|
||||
0,
|
||||
0.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh":1,
|
||||
"name":"KeyMesh",
|
||||
"skin":0
|
||||
},
|
||||
{
|
||||
"children":[
|
||||
5,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
],
|
||||
"name":"KeyArmature"
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"NubMesh",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"NORMAL":1,
|
||||
"TEXCOORD_0":2
|
||||
},
|
||||
"indices":3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"KeyMesh",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":4,
|
||||
"NORMAL":5,
|
||||
"TEXCOORD_0":6,
|
||||
"JOINTS_0":7,
|
||||
"WEIGHTS_0":8
|
||||
},
|
||||
"indices":9
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"skins":[
|
||||
{
|
||||
"inverseBindMatrices":10,
|
||||
"joints":[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
],
|
||||
"name":"KeyArmature"
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":25,
|
||||
"max":[
|
||||
0.15000000596046448,
|
||||
0.03999999910593033,
|
||||
0.02499999664723873
|
||||
],
|
||||
"min":[
|
||||
-0.15000000596046448,
|
||||
0,
|
||||
-0.02499999664723873
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":25,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":25,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":90,
|
||||
"type":"SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView":4,
|
||||
"componentType":5126,
|
||||
"count":115,
|
||||
"max":[
|
||||
0.5,
|
||||
0.2500000596046448,
|
||||
0.5
|
||||
],
|
||||
"min":[
|
||||
-0.5,
|
||||
-0.25000008940696716,
|
||||
-0.5
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":5,
|
||||
"componentType":5126,
|
||||
"count":115,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":6,
|
||||
"componentType":5126,
|
||||
"count":115,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":7,
|
||||
"componentType":5121,
|
||||
"count":115,
|
||||
"type":"VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView":8,
|
||||
"componentType":5126,
|
||||
"count":115,
|
||||
"type":"VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView":9,
|
||||
"componentType":5123,
|
||||
"count":564,
|
||||
"type":"SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView":10,
|
||||
"componentType":5126,
|
||||
"count":4,
|
||||
"type":"MAT4"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":300,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":300,
|
||||
"byteOffset":300,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":200,
|
||||
"byteOffset":600,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":180,
|
||||
"byteOffset":800,
|
||||
"target":34963
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":1380,
|
||||
"byteOffset":980,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":1380,
|
||||
"byteOffset":2360,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":920,
|
||||
"byteOffset":3740,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":460,
|
||||
"byteOffset":4660,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":1840,
|
||||
"byteOffset":5120,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":1128,
|
||||
"byteOffset":6960,
|
||||
"target":34963
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":256,
|
||||
"byteOffset":8088
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":8344,
|
||||
"uri":"key.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -3,13 +3,13 @@
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bmswlpu3ym25i"
|
||||
path="res://.godot/imported/key.glb-28a29c675eb9833cbdb70f3e33f99c5f.scn"
|
||||
uid="uid://d4ffyk6iou3ns"
|
||||
path="res://.godot/imported/key.gltf-680011bf56a1e8dedf6b8edbd0ccfade.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/key.glb"
|
||||
dest_files=["res://.godot/imported/key.glb-28a29c675eb9833cbdb70f3e33f99c5f.scn"]
|
||||
source_file="res://assets/models/key.gltf"
|
||||
dest_files=["res://.godot/imported/key.gltf-680011bf56a1e8dedf6b8edbd0ccfade.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
5
assets/sfx/THIRDPARTY.md
Normal file
5
assets/sfx/THIRDPARTY.md
Normal file
@ -0,0 +1,5 @@
|
||||
The Hollywood Edge - The Super Single Volume 1
|
||||
- keyboard/typing/*
|
||||
|
||||
Zero-G - Datafile 1
|
||||
- player/move/*
|
||||
BIN
assets/sfx/player/move/move_1.wav
(Stored with Git LFS)
Normal file
BIN
assets/sfx/player/move/move_1.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
24
assets/sfx/player/move/move_1.wav.import
Normal file
24
assets/sfx/player/move/move_1.wav.import
Normal file
@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://86cbncjjh15f"
|
||||
path="res://.godot/imported/move_1.wav-56d884599c9cde94cc568eebc198ee3b.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sfx/player/move/move_1.wav"
|
||||
dest_files=["res://.godot/imported/move_1.wav-56d884599c9cde94cc568eebc198ee3b.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=true
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
BIN
assets/sfx/player/move/move_2.wav
(Stored with Git LFS)
Normal file
BIN
assets/sfx/player/move/move_2.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
24
assets/sfx/player/move/move_2.wav.import
Normal file
24
assets/sfx/player/move/move_2.wav.import
Normal file
@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://daqhk1ae8oocx"
|
||||
path="res://.godot/imported/move_2.wav-9b2ccb06ce79e3adc46b87f25810f7b7.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sfx/player/move/move_2.wav"
|
||||
dest_files=["res://.godot/imported/move_2.wav-9b2ccb06ce79e3adc46b87f25810f7b7.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=true
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
BIN
assets/sfx/player/move/move_3.wav
(Stored with Git LFS)
Normal file
BIN
assets/sfx/player/move/move_3.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
24
assets/sfx/player/move/move_3.wav.import
Normal file
24
assets/sfx/player/move/move_3.wav.import
Normal file
@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://c02pnx1fm8bqa"
|
||||
path="res://.godot/imported/move_3.wav-9b07a720ee30a308d65b101571150e05.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sfx/player/move/move_3.wav"
|
||||
dest_files=["res://.godot/imported/move_3.wav-9b07a720ee30a308d65b101571150e05.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=true
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
BIN
assets/sfx/player/move/move_4.wav
(Stored with Git LFS)
Normal file
BIN
assets/sfx/player/move/move_4.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
24
assets/sfx/player/move/move_4.wav.import
Normal file
24
assets/sfx/player/move/move_4.wav.import
Normal file
@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://dmoypbjc535kp"
|
||||
path="res://.godot/imported/move_4.wav-2bb98dafa59430d4852b755f935f8454.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sfx/player/move/move_4.wav"
|
||||
dest_files=["res://.godot/imported/move_4.wav-2bb98dafa59430d4852b755f935f8454.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=true
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=2
|
||||
@ -1,6 +1,7 @@
|
||||
[gd_resource type="AudioBusLayout" format=3 uid="uid://dhchfbeo7pxbe"]
|
||||
|
||||
[resource]
|
||||
bus/0/volume_db = -11.952
|
||||
bus/1/name = &"SFX"
|
||||
bus/1/solo = false
|
||||
bus/1/mute = false
|
||||
|
||||
@ -7,7 +7,7 @@ advanced_options=true
|
||||
dedicated_server=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
include_filter="resources/kle/README.txt"
|
||||
exclude_filter=""
|
||||
export_path="build/windows/keebie.exe"
|
||||
patches=PackedStringArray()
|
||||
|
||||
@ -11,7 +11,7 @@ config_version=5
|
||||
[application]
|
||||
|
||||
config/name="keebie"
|
||||
config/version="0.0.1"
|
||||
config/version="0.0.2"
|
||||
run/main_scene="uid://djmj08xtpgpgk"
|
||||
config/use_custom_user_dir=true
|
||||
config/custom_user_dir_name="keebie"
|
||||
@ -57,6 +57,11 @@ reset_animations={
|
||||
"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":4194340,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
toggle_configuring={
|
||||
"deadzone": 0.2,
|
||||
"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":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[physics]
|
||||
|
||||
|
||||
36
resources/custom_layouts/README.txt
Normal file
36
resources/custom_layouts/README.txt
Normal file
@ -0,0 +1,36 @@
|
||||
Here you can add custom physical layouts.
|
||||
Beware that wacky layouts may break the game.
|
||||
|
||||
|
||||
|
||||
You can use Keyboard Layout Editor to create and edit layouts:
|
||||
https://www.keyboard-layout-editor.com
|
||||
|
||||
Once you have finished making the layout, export it in JSON format (Download > Download JSON)
|
||||
and put the JSON file in this directory.
|
||||
|
||||
Keep in mind that key labels should adhere to the standard US QWERTY layout,
|
||||
so that the game could correctly infer their keycodes.
|
||||
|
||||
|
||||
|
||||
Also you can use QMK Firmware keyboard configurations:
|
||||
https://qmk.fm
|
||||
|
||||
You'll need to take the keyboard's info.json (to get the layout) and keymap.c (to get the keycodes),
|
||||
rename them to have the same file name (e.g. my-keyboard.json and my-keyboard.c)
|
||||
and put them in this directory.
|
||||
|
||||
If your keyboard has already been added to QMK, you can get its configuration here:
|
||||
https://github.com/qmk/qmk_firmware/tree/master/keyboards
|
||||
|
||||
The game will only read the first occurring layout definition and keymap layer,
|
||||
so you may need to edit them and move the ones you want to be the first.
|
||||
|
||||
Also if your keymap.c has language-specific keycode aliases, you will need to replace them
|
||||
with actual keycodes corresponding to the standard US QWERTY layout,
|
||||
so that the game could correctly infer them.
|
||||
|
||||
|
||||
|
||||
If the game doesn't load the layout, check the log file for errors (../logs/godot.log).
|
||||
@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"name": "ANSI 104 (big enter)"
|
||||
"name": "ANSI 104 (Big-ass Enter)"
|
||||
},
|
||||
[
|
||||
"Esc",
|
||||
@ -116,9 +116,15 @@
|
||||
"A",
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
@ -113,9 +113,15 @@
|
||||
"A",
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
313
resources/custom_layouts/apple-wireless-keyboard.json
Normal file
313
resources/custom_layouts/apple-wireless-keyboard.json
Normal file
@ -0,0 +1,313 @@
|
||||
[
|
||||
{
|
||||
"backcolor": "#dbdbdb",
|
||||
"name": "Apple Wireless Keyboard",
|
||||
"author": "Alistair Calder",
|
||||
"radii": "6px 6px 12px 12px / 18px 18px 12px 12px",
|
||||
"css": "@import url(http://fonts.googleapis.com/css?family=Varela+Round);\n\n#keyboard-bg { \n background-image: linear-gradient(to bottom, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 4%, rgba(255,255,255,0.3) 6%, rgba(0,0,0,0) 10%), \n linear-gradient(to right, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 100%) !important; \n}\n\n.keylabel {\n font-family: 'volkswagen_serialregular';\n}\n\n/* Strangely, \"Volkswagen Serial\" doesn't have a tilde character */\n.varela { \n font-family: 'Varela Round'; \n display: inline-block; \n font-size: inherit; \n text-rendering: auto; \n -webkit-font-smoothing: antialiased; \n -moz-osx-font-smoothing: grayscale;\n transform: translate(0, 0);\n}\n.varela-tilde:after { content: \"\\07e\"; }"
|
||||
},
|
||||
[
|
||||
{
|
||||
"y": 0.75,
|
||||
"t": "#666666",
|
||||
"p": "CHICKLET",
|
||||
"a": 7,
|
||||
"f": 2,
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"esc",
|
||||
{
|
||||
"a": 4,
|
||||
"fa": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF1",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF2",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF3",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF4",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF5",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF6",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF7\n\n\n\n\n\n<i class='fa fa-backward'></i>",
|
||||
{
|
||||
"fa": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF8\n\n\n\n\n\n<i class='fa fa-play'></i><i class='fa fa-pause'></i>",
|
||||
{
|
||||
"fa": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF9\n\n\n\n\n\n<i class='fa fa-forward'></i>",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF10\n\n\n\n\n\n<i class='fa fa-volume-off'></i>",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF11\n\n\n\n\n\n<i class='fa fa-volume-down'></i>",
|
||||
{
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"\n\n\nF12\n\n\n\n\n\n<i class='fa fa-volume-up'></i>",
|
||||
{
|
||||
"a": 7,
|
||||
"w": 1.0357,
|
||||
"h": 0.75
|
||||
},
|
||||
"<i class='fa fa-eject'></i>"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.25,
|
||||
"a": 5,
|
||||
"f": 5
|
||||
},
|
||||
"<i class=\"varela varela-tilde\"></i>\n`",
|
||||
"!\n1",
|
||||
"@\n2",
|
||||
"#\n3",
|
||||
"$\n4",
|
||||
"%\n5",
|
||||
"^\n6",
|
||||
"&\n7",
|
||||
"*\n8",
|
||||
"(\n9",
|
||||
")\n0",
|
||||
"_\n-",
|
||||
"+\n=",
|
||||
{
|
||||
"a": 4,
|
||||
"f": 2,
|
||||
"w": 1.5
|
||||
},
|
||||
"\n\n\ndelete"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"\ntab",
|
||||
{
|
||||
"a": 7,
|
||||
"f": 5
|
||||
},
|
||||
"Q",
|
||||
"W",
|
||||
"E",
|
||||
"R",
|
||||
"T",
|
||||
"Y",
|
||||
"U",
|
||||
"I",
|
||||
"O",
|
||||
"P",
|
||||
{
|
||||
"a": 5
|
||||
},
|
||||
"{\n[",
|
||||
"}\n]",
|
||||
"|\n\\"
|
||||
],
|
||||
[
|
||||
{
|
||||
"a": 4,
|
||||
"f": 2,
|
||||
"fa": [
|
||||
1
|
||||
],
|
||||
"w": 1.75
|
||||
},
|
||||
"<i class='kb kb-Multimedia-Record'></i>\ncaps lock",
|
||||
{
|
||||
"a": 7,
|
||||
"f": 5
|
||||
},
|
||||
"A",
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
{
|
||||
"a": 5
|
||||
},
|
||||
":\n;",
|
||||
"\"\n'",
|
||||
{
|
||||
"a": 4,
|
||||
"f": 2,
|
||||
"fa": [
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"w": 1.75
|
||||
},
|
||||
"\n\nenter\nreturn"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 2.25
|
||||
},
|
||||
"\nshift",
|
||||
{
|
||||
"a": 7,
|
||||
"f": 5
|
||||
},
|
||||
"Z",
|
||||
"X",
|
||||
"C",
|
||||
"V",
|
||||
"B",
|
||||
"N",
|
||||
"M",
|
||||
{
|
||||
"a": 5
|
||||
},
|
||||
"<\n,",
|
||||
">\n.",
|
||||
"?\n/",
|
||||
{
|
||||
"a": 4,
|
||||
"f": 2,
|
||||
"w": 2.25
|
||||
},
|
||||
"\n\n\nshift"
|
||||
],
|
||||
[
|
||||
{
|
||||
"h": 1.111
|
||||
},
|
||||
"\nfn",
|
||||
{
|
||||
"h": 1.111
|
||||
},
|
||||
"\ncontrol",
|
||||
{
|
||||
"fa": [
|
||||
1
|
||||
],
|
||||
"h": 1.111
|
||||
},
|
||||
"alt\noption",
|
||||
{
|
||||
"fa": [
|
||||
1,
|
||||
0,
|
||||
5
|
||||
],
|
||||
"w": 1.25,
|
||||
"h": 1.111
|
||||
},
|
||||
"\n\n⌘\ncommand",
|
||||
{
|
||||
"a": 7,
|
||||
"w": 5,
|
||||
"h": 1.111
|
||||
},
|
||||
"",
|
||||
{
|
||||
"a": 4,
|
||||
"fa": [
|
||||
5
|
||||
],
|
||||
"w": 1.25,
|
||||
"h": 1.111
|
||||
},
|
||||
"⌘\ncommand",
|
||||
{
|
||||
"fa": [
|
||||
5,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"h": 1.111
|
||||
},
|
||||
"\n\nalt\noption",
|
||||
{
|
||||
"x": 1,
|
||||
"a": 7,
|
||||
"f": 5,
|
||||
"h": 0.611
|
||||
},
|
||||
"↑"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.5,
|
||||
"x": 11.5,
|
||||
"h": 0.6111
|
||||
},
|
||||
"←",
|
||||
{
|
||||
"h": 0.6111
|
||||
},
|
||||
"↓",
|
||||
{
|
||||
"h": 0.6111
|
||||
},
|
||||
"→"
|
||||
]
|
||||
]
|
||||
276
resources/custom_layouts/atreus.json
Normal file
276
resources/custom_layouts/atreus.json
Normal file
@ -0,0 +1,276 @@
|
||||
[
|
||||
{
|
||||
"name": "Atreus"
|
||||
},
|
||||
[
|
||||
{
|
||||
"r": 10,
|
||||
"rx": 1,
|
||||
"y": -0.09999999999999998,
|
||||
"x": 2
|
||||
},
|
||||
"E"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.65,
|
||||
"x": 1
|
||||
},
|
||||
"W",
|
||||
{
|
||||
"x": 1
|
||||
},
|
||||
"R"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75
|
||||
},
|
||||
"Q"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.9,
|
||||
"x": 4
|
||||
},
|
||||
"T"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.7000000000000001,
|
||||
"x": 2
|
||||
},
|
||||
"D"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.6499999999999999,
|
||||
"x": 1
|
||||
},
|
||||
"S",
|
||||
{
|
||||
"x": 1,
|
||||
"n": true
|
||||
},
|
||||
"F"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75
|
||||
},
|
||||
"A"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.8999999999999999,
|
||||
"x": 4
|
||||
},
|
||||
"G"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.7000000000000002,
|
||||
"x": 2
|
||||
},
|
||||
"C"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.6499999999999999,
|
||||
"x": 1
|
||||
},
|
||||
"X",
|
||||
{
|
||||
"x": 1
|
||||
},
|
||||
"V"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75
|
||||
},
|
||||
"Z"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.8999999999999999,
|
||||
"x": 4
|
||||
},
|
||||
"B"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"x": 5,
|
||||
"h": 1.5
|
||||
},
|
||||
"Ctrl"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.9500000000000002,
|
||||
"x": 2
|
||||
},
|
||||
"super"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.6499999999999999,
|
||||
"x": 1
|
||||
},
|
||||
"Tab",
|
||||
{
|
||||
"x": 1
|
||||
},
|
||||
"Shift"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75
|
||||
},
|
||||
"Esc"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.8999999999999999,
|
||||
"x": 4
|
||||
},
|
||||
"Bksp"
|
||||
],
|
||||
[
|
||||
{
|
||||
"r": -10,
|
||||
"rx": 7,
|
||||
"ry": 0.965,
|
||||
"y": -0.20000000000000018,
|
||||
"x": 2
|
||||
},
|
||||
"I"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.6499999999999999,
|
||||
"x": 1
|
||||
},
|
||||
"U",
|
||||
{
|
||||
"x": 1
|
||||
},
|
||||
"O"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"x": 4
|
||||
},
|
||||
"P"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.8999999999999999
|
||||
},
|
||||
"Y"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.7000000000000002,
|
||||
"x": 2
|
||||
},
|
||||
"K"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.6499999999999999,
|
||||
"x": 1,
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
{
|
||||
"x": 1
|
||||
},
|
||||
"L"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"x": 4
|
||||
},
|
||||
":\n;"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.8999999999999999
|
||||
},
|
||||
"H"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.7000000000000002,
|
||||
"x": 2
|
||||
},
|
||||
"<\n,"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.6499999999999999,
|
||||
"x": 1
|
||||
},
|
||||
"M",
|
||||
{
|
||||
"x": 1
|
||||
},
|
||||
">\n."
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.7500000000000004,
|
||||
"x": 4
|
||||
},
|
||||
"?\n/"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.9000000000000004
|
||||
},
|
||||
"N"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.7499999999999996,
|
||||
"x": -1,
|
||||
"h": 1.5
|
||||
},
|
||||
"Alt"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.9499999999999997,
|
||||
"x": 2
|
||||
},
|
||||
"_\n-"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.6500000000000004,
|
||||
"x": 1
|
||||
},
|
||||
"fn",
|
||||
{
|
||||
"x": 1
|
||||
},
|
||||
"\"\n'"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"x": 4
|
||||
},
|
||||
"Enter"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.9000000000000004
|
||||
},
|
||||
"Space"
|
||||
]
|
||||
]
|
||||
@ -118,9 +118,15 @@
|
||||
"A",
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
85
resources/custom_layouts/jd40.json
Normal file
85
resources/custom_layouts/jd40.json
Normal file
@ -0,0 +1,85 @@
|
||||
[
|
||||
{
|
||||
"name": "JD40"
|
||||
},
|
||||
[
|
||||
"Esc",
|
||||
"Q",
|
||||
"W",
|
||||
"E",
|
||||
"R",
|
||||
"T",
|
||||
"Y",
|
||||
"U",
|
||||
"I",
|
||||
"O",
|
||||
"P",
|
||||
"Back<br>Space"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"Tab",
|
||||
"A",
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
{
|
||||
"w": 1.75
|
||||
},
|
||||
"Enter"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 1.75
|
||||
},
|
||||
"Shift",
|
||||
"Z",
|
||||
"X",
|
||||
"C",
|
||||
"V",
|
||||
"B",
|
||||
"N",
|
||||
"M",
|
||||
"<\n.",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"Shift",
|
||||
"Fn"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"Hyper",
|
||||
"Super",
|
||||
"Meta",
|
||||
{
|
||||
"a": 7,
|
||||
"w": 6.25
|
||||
},
|
||||
"",
|
||||
{
|
||||
"a": 4,
|
||||
"w": 1.25
|
||||
},
|
||||
"Meta",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"Super"
|
||||
]
|
||||
]
|
||||
@ -86,9 +86,15 @@
|
||||
"A",
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
381
resources/custom_layouts/kinesis-advantage.json
Normal file
381
resources/custom_layouts/kinesis-advantage.json
Normal file
@ -0,0 +1,381 @@
|
||||
[
|
||||
{
|
||||
"name": "Kinesis Advantage"
|
||||
},
|
||||
[
|
||||
{
|
||||
"f": 1,
|
||||
"f2": 2,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nEsc",
|
||||
{
|
||||
"x": 0.07499999999999996,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nF1",
|
||||
{
|
||||
"x": 0.07499999999999996,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nF2",
|
||||
{
|
||||
"x": 0.07500000000000018,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nF3",
|
||||
{
|
||||
"x": 0.07500000000000018,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nF4",
|
||||
{
|
||||
"x": 0.07500000000000018,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nF5",
|
||||
{
|
||||
"x": 0.07500000000000018,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nF6",
|
||||
{
|
||||
"x": 0.07500000000000018,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nF7",
|
||||
{
|
||||
"x": 0.07500000000000018,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"\nF8",
|
||||
{
|
||||
"x": 4.825,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"Repeat Rate\nF9",
|
||||
{
|
||||
"x": 0.07499999999999929,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"Disable Macro\nF10",
|
||||
{
|
||||
"x": 0.07499999999999929,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"Macro\nF11",
|
||||
{
|
||||
"x": 0.07499999999999929,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"Remap\nF12",
|
||||
{
|
||||
"x": 0.07499999999999929,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"PrintScr SysReq",
|
||||
{
|
||||
"x": 0.07499999999999929,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"Scroll<br>lock",
|
||||
{
|
||||
"x": 0.07499999999999929,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"Pause Break",
|
||||
{
|
||||
"x": 0.07499999999999929,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"Keypad",
|
||||
{
|
||||
"x": 0.07499999999999929,
|
||||
"w": 0.675,
|
||||
"h": 0.85
|
||||
},
|
||||
"Progrm"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 2.25,
|
||||
"f": 3
|
||||
},
|
||||
"@\n2",
|
||||
"#\n3",
|
||||
"$\n4",
|
||||
"%\n5",
|
||||
{
|
||||
"x": 5.5
|
||||
},
|
||||
"^\n6",
|
||||
"&\n7\n\n\nNm Lk",
|
||||
"*\n8\n\n\n=",
|
||||
"(\n9\n\n\n="
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"w": 1.25
|
||||
},
|
||||
"+\n=",
|
||||
"!\n1",
|
||||
{
|
||||
"x": 13.5
|
||||
},
|
||||
")\n0\n\n\n*",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"_\n-"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.25,
|
||||
"x": 2.25,
|
||||
"f": 6
|
||||
},
|
||||
"W",
|
||||
"E",
|
||||
"R",
|
||||
"T",
|
||||
{
|
||||
"x": 5.5
|
||||
},
|
||||
"Y",
|
||||
"U\n\n\n\n7",
|
||||
"I\n\n\n\n8",
|
||||
"O\n\n\n\n9"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"a": 6,
|
||||
"f": 3,
|
||||
"w": 1.25
|
||||
},
|
||||
"Tab",
|
||||
{
|
||||
"a": 4,
|
||||
"f": 6
|
||||
},
|
||||
"Q",
|
||||
{
|
||||
"x": 13.5
|
||||
},
|
||||
"P\n\n\n\n-",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 1.25
|
||||
},
|
||||
"|\n\\"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.25,
|
||||
"x": 2.25,
|
||||
"f": 6
|
||||
},
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
{
|
||||
"x": 5.5
|
||||
},
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J\n\n\n\n4",
|
||||
"K\n\n\n\n5",
|
||||
"L\n\n\n\n6"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"a": 6,
|
||||
"f": 3,
|
||||
"w": 1.25
|
||||
},
|
||||
"Caps<br>Lock",
|
||||
{
|
||||
"a": 4,
|
||||
"f": 6
|
||||
},
|
||||
"A",
|
||||
{
|
||||
"x": 13.5,
|
||||
"f": 3
|
||||
},
|
||||
":\n;\n\n\n+",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"\"\n'"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.25,
|
||||
"x": 2.25,
|
||||
"f": 6
|
||||
},
|
||||
"X",
|
||||
"C",
|
||||
"V",
|
||||
"B",
|
||||
{
|
||||
"x": 5.5
|
||||
},
|
||||
"N",
|
||||
"M\n\n\n\n1",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"<\n,\n\n\n2",
|
||||
">\n.\n\n\n3"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"a": 6,
|
||||
"w": 1.25
|
||||
},
|
||||
"Shift",
|
||||
{
|
||||
"a": 4,
|
||||
"f": 6
|
||||
},
|
||||
"Z",
|
||||
{
|
||||
"x": 13.5,
|
||||
"f": 3
|
||||
},
|
||||
"?\n/\n\n\nEnter",
|
||||
{
|
||||
"a": 6,
|
||||
"w": 1.25
|
||||
},
|
||||
"Shift"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.25,
|
||||
"x": 2.25,
|
||||
"a": 4
|
||||
},
|
||||
"|\n\\\n\n\nInsert",
|
||||
{
|
||||
"a": 5,
|
||||
"f": 5
|
||||
},
|
||||
"⇦\n\n\n\n⇦",
|
||||
"⇨\n\n\n\n⇨",
|
||||
{
|
||||
"x": 7.5
|
||||
},
|
||||
"⇧\n\n\n\n⇧",
|
||||
"⇩\n\n\n\n⇩",
|
||||
{
|
||||
"a": 4,
|
||||
"f": 3
|
||||
},
|
||||
"{\n[\n\n\n."
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.75,
|
||||
"x": 1.25
|
||||
},
|
||||
"~\n`",
|
||||
{
|
||||
"x": 13.5
|
||||
},
|
||||
"}\n]\n\n\nEnter"
|
||||
],
|
||||
[
|
||||
{
|
||||
"r": 15,
|
||||
"rx": 5.25,
|
||||
"ry": 4,
|
||||
"x": 1.5
|
||||
},
|
||||
"Ctrl",
|
||||
"Alt"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 0.5,
|
||||
"a": 7,
|
||||
"h": 2
|
||||
},
|
||||
"Back<br>Space",
|
||||
{
|
||||
"h": 2
|
||||
},
|
||||
"Delete",
|
||||
"Home"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 2.5
|
||||
},
|
||||
"End"
|
||||
],
|
||||
[
|
||||
{
|
||||
"r": -15,
|
||||
"rx": 12.75,
|
||||
"x": -3.5,
|
||||
"a": 4
|
||||
},
|
||||
"Cmd\n\n\n\n\n\nWin",
|
||||
{
|
||||
"a": 7
|
||||
},
|
||||
"Ctrl"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": -3.5,
|
||||
"a": 6
|
||||
},
|
||||
"Page<br>Up",
|
||||
{
|
||||
"a": 7,
|
||||
"h": 2
|
||||
},
|
||||
"Enter",
|
||||
{
|
||||
"h": 2
|
||||
},
|
||||
"Space"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": -3.5,
|
||||
"a": 6
|
||||
},
|
||||
"Page<br>Down"
|
||||
]
|
||||
]
|
||||
408
resources/custom_layouts/lenovo-loq.json
Normal file
408
resources/custom_layouts/lenovo-loq.json
Normal file
@ -0,0 +1,408 @@
|
||||
[
|
||||
{
|
||||
"name": "Lenovo LOQ"
|
||||
},
|
||||
[
|
||||
{
|
||||
"f2": 1,
|
||||
"h": 0.666
|
||||
},
|
||||
"Esc\nFnLock",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F1",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F2",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F3",
|
||||
{
|
||||
"x": 4.440892098500626e-16,
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F4",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F5",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F6",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F7",
|
||||
{
|
||||
"x": -8.881784197001252e-16,
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F8",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F9",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F10",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F11",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"F12",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"Insert",
|
||||
{
|
||||
"w": 0.8833,
|
||||
"h": 0.666
|
||||
},
|
||||
"PrtSc",
|
||||
{
|
||||
"x": -1.7763568394002505e-15,
|
||||
"h": 0.666
|
||||
},
|
||||
"Delete",
|
||||
{
|
||||
"w": 0.833,
|
||||
"h": 0.666
|
||||
},
|
||||
"Home",
|
||||
{
|
||||
"w": 0.833,
|
||||
"h": 0.666
|
||||
},
|
||||
"End",
|
||||
{
|
||||
"w": 0.833,
|
||||
"h": 0.666
|
||||
},
|
||||
"PgUp",
|
||||
{
|
||||
"x": 3.552713678800501e-15,
|
||||
"w": 0.833,
|
||||
"h": 0.666
|
||||
},
|
||||
"PgDn"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.33399999999999996,
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"~\n`",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"!\n1",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"@\n2",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"#\n3",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"$\n4",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"%\n5",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"^\n6",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"&\n7",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"*\n8",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"(\n9",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
")\n0",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"_\n-",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"+\n=",
|
||||
{
|
||||
"w": 1.5332
|
||||
},
|
||||
"Backspace"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.9959999999999999,
|
||||
"x": 14.3662,
|
||||
"w": 0.833
|
||||
},
|
||||
"Num Lock",
|
||||
{
|
||||
"w": 0.833
|
||||
},
|
||||
"/",
|
||||
{
|
||||
"w": 0.833
|
||||
},
|
||||
"*",
|
||||
{
|
||||
"x": 3.552713678800501e-15,
|
||||
"w": 0.833
|
||||
},
|
||||
"-"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.0040000000000000036,
|
||||
"w": 1.25
|
||||
},
|
||||
"Tab"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.996,
|
||||
"x": 1.25
|
||||
},
|
||||
"Q",
|
||||
"W",
|
||||
"E",
|
||||
"R",
|
||||
"T",
|
||||
"Y",
|
||||
"U",
|
||||
"I",
|
||||
"O",
|
||||
"P",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"{\n[",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"}\n]",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 1.1162
|
||||
},
|
||||
"|\n\\",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"7\nHome",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"8\n↑",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"9\nPgUp",
|
||||
{
|
||||
"x": 3.552713678800501e-15,
|
||||
"w": 0.833,
|
||||
"h": 2
|
||||
},
|
||||
"+"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.009999999999999787,
|
||||
"w": 1.5
|
||||
},
|
||||
"Caps Lock",
|
||||
"A",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
":\n;",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"\"\n'",
|
||||
{
|
||||
"w": 1.8662
|
||||
},
|
||||
"Enter",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"4\n←",
|
||||
{
|
||||
"w": 0.833
|
||||
},
|
||||
"5",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"6\n→"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": 0.009999999999999787,
|
||||
"w": 2
|
||||
},
|
||||
"Shift",
|
||||
"Z",
|
||||
"X",
|
||||
"C",
|
||||
"V",
|
||||
"B",
|
||||
"N",
|
||||
"M",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
"<\n,",
|
||||
{
|
||||
"f": 3
|
||||
},
|
||||
">\n.",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 1.122
|
||||
},
|
||||
"?\n/",
|
||||
{
|
||||
"w": 2.244
|
||||
},
|
||||
"Shift",
|
||||
{
|
||||
"x": 0.00019999999999953388,
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"1\nEnd",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"2\n↓",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
"3\nPgDn",
|
||||
{
|
||||
"x": 3.552713678800501e-15,
|
||||
"w": 0.833,
|
||||
"h": 2
|
||||
},
|
||||
"Enter"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.0039999999999995595
|
||||
},
|
||||
"Ctrl"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": -0.9960000000000004,
|
||||
"x": 1
|
||||
},
|
||||
"Fn",
|
||||
"Win",
|
||||
"Alt",
|
||||
{
|
||||
"a": 7,
|
||||
"w": 5
|
||||
},
|
||||
"",
|
||||
{
|
||||
"a": 4
|
||||
},
|
||||
"Alt",
|
||||
"Win",
|
||||
{
|
||||
"x": 1.1219999999999999,
|
||||
"w": 1.122
|
||||
},
|
||||
"↑",
|
||||
{
|
||||
"x": 1.1221999999999994,
|
||||
"f": 3,
|
||||
"w": 1.666
|
||||
},
|
||||
"0\nIns",
|
||||
{
|
||||
"f": 3,
|
||||
"w": 0.833
|
||||
},
|
||||
".\nDel"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 11,
|
||||
"w": 1.122
|
||||
},
|
||||
"←",
|
||||
{
|
||||
"w": 1.122
|
||||
},
|
||||
"↓",
|
||||
{
|
||||
"w": 1.122
|
||||
},
|
||||
"→"
|
||||
]
|
||||
]
|
||||
72
resources/custom_layouts/planck.json
Normal file
72
resources/custom_layouts/planck.json
Normal file
@ -0,0 +1,72 @@
|
||||
[
|
||||
{
|
||||
"name": "Planck"
|
||||
},
|
||||
[
|
||||
{
|
||||
"a": 7
|
||||
},
|
||||
"Tab",
|
||||
"Q",
|
||||
"W",
|
||||
"E",
|
||||
"R",
|
||||
"T",
|
||||
"Y",
|
||||
"U",
|
||||
"I",
|
||||
"O",
|
||||
"P",
|
||||
"Back Space"
|
||||
],
|
||||
[
|
||||
"Esc",
|
||||
"A",
|
||||
"S",
|
||||
"D",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
{
|
||||
"n": true
|
||||
},
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
";",
|
||||
"'"
|
||||
],
|
||||
[
|
||||
"Shift",
|
||||
"Z",
|
||||
"X",
|
||||
"C",
|
||||
"V",
|
||||
"B",
|
||||
"N",
|
||||
"M",
|
||||
",",
|
||||
".",
|
||||
"/",
|
||||
"Return"
|
||||
],
|
||||
[
|
||||
"",
|
||||
"Ctrl",
|
||||
"Alt",
|
||||
"Super",
|
||||
"⇓",
|
||||
{
|
||||
"w": 2
|
||||
},
|
||||
"",
|
||||
"⇑",
|
||||
"←",
|
||||
"↓",
|
||||
"↑",
|
||||
"→"
|
||||
]
|
||||
]
|
||||
@ -1,5 +1,6 @@
|
||||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://dkydluksv2ry0"]
|
||||
|
||||
[resource]
|
||||
resource_local_to_scene = true
|
||||
albedo_color = Color(0.125911, 0.125911, 0.125911, 1)
|
||||
roughness = 0.6
|
||||
|
||||
@ -22,6 +22,7 @@ _typing_label = NodePath("TypingLabel")
|
||||
_layout_swap_label = NodePath("LayoutSwapLabel")
|
||||
|
||||
[node name="InputLabel" type="RichTextLabel" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 14.0
|
||||
offset_top = 15.0
|
||||
@ -56,9 +57,9 @@ anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -400.0
|
||||
offset_top = 299.0
|
||||
offset_top = 341.0
|
||||
offset_right = 400.0
|
||||
offset_bottom = -713.0
|
||||
offset_bottom = -524.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
@ -76,9 +77,9 @@ anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -400.0
|
||||
offset_top = 401.0
|
||||
offset_top = 422.0
|
||||
offset_right = 400.0
|
||||
offset_bottom = -616.0
|
||||
offset_bottom = -421.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
[gd_scene load_steps=20 format=3 uid="uid://bryima34hc3yp"]
|
||||
[gd_scene load_steps=21 format=3 uid="uid://bryima34hc3yp"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cl0asbgqd3anu" path="res://scripts/game_key.gd" id="1_sypr4"]
|
||||
[ext_resource type="PackedScene" uid="uid://bmswlpu3ym25i" path="res://assets/models/key.glb" id="2_6rsff"]
|
||||
[ext_resource type="AudioStream" uid="uid://bq0ikue882kg6" path="res://assets/sfx/keyboard/typing/press_01.wav" id="2_cqjrw"]
|
||||
[ext_resource type="AudioStream" uid="uid://dgndsx3o21yss" path="res://assets/sfx/keyboard/typing/press_02.wav" id="3_p6qfn"]
|
||||
[ext_resource type="AudioStream" uid="uid://1l34knbt0avm" path="res://assets/sfx/keyboard/typing/press_03.wav" id="4_y31qj"]
|
||||
@ -16,6 +15,8 @@
|
||||
[ext_resource type="AudioStream" uid="uid://dj7cpa4c8thp6" path="res://assets/sfx/keyboard/typing/release_05.wav" id="13_tj68p"]
|
||||
[ext_resource type="AudioStream" uid="uid://41d8y11au44e" path="res://assets/sfx/keyboard/typing/release_06.wav" id="14_kt0yx"]
|
||||
[ext_resource type="AudioStream" uid="uid://8rgf5ic4w1r3" path="res://assets/sfx/keyboard/typing/release_07.wav" id="15_w7o33"]
|
||||
[ext_resource type="PackedScene" uid="uid://d4ffyk6iou3ns" path="res://assets/models/key.gltf" id="16_cqjrw"]
|
||||
[ext_resource type="Material" uid="uid://dkydluksv2ry0" path="res://resources/materials/key_mat.tres" id="17_p6qfn"]
|
||||
|
||||
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_agrko"]
|
||||
random_pitch = 1.1
|
||||
@ -41,39 +42,55 @@ stream_6/stream = ExtResource("15_w7o33")
|
||||
|
||||
[sub_resource type="AudioStreamPolyphonic" id="AudioStreamPolyphonic_6rsff"]
|
||||
|
||||
[node name="GameKey" type="Node3D" node_paths=PackedStringArray("_skeleton_primary", "_skeleton_secondary", "_upper_left_label", "_upper_right_label", "_lower_left_label", "_lower_right_label", "_center_label", "_press_light", "_sfx_player")]
|
||||
[node name="GameKey" type="Node3D" node_paths=PackedStringArray("player_pos_marker", "_skeleton_primary", "_skeleton_secondary", "_nub_mesh", "_upper_left_label", "_upper_right_label", "_lower_left_label", "_lower_right_label", "_center_label", "_press_light", "_adjacency_light", "_sfx_player")]
|
||||
script = ExtResource("1_sypr4")
|
||||
_skeleton_primary = NodePath("ModelPrimary/KeyArmature/Skeleton3D")
|
||||
player_pos_marker = NodePath("PlayerPosMarker")
|
||||
_skeleton_primary = NodePath("Model/KeyArmature/Skeleton3D")
|
||||
_skeleton_secondary = NodePath("ModelSecondary/KeyArmature/Skeleton3D")
|
||||
_upper_left_label = NodePath("UpperLeftAttachment/UpperLeftLabel")
|
||||
_upper_right_label = NodePath("UpperRightAttachment/UpperRightLabel")
|
||||
_lower_left_label = NodePath("LowerLeftAttachment/LowerLeftLabel")
|
||||
_lower_right_label = NodePath("LowerRightAttachment/LowerRightLabel")
|
||||
_center_label = NodePath("CenterLabel")
|
||||
_press_light = NodePath("PressLight")
|
||||
_nub_mesh = NodePath("Model/NubMesh")
|
||||
_upper_left_label = NodePath("Labels/UpperLeftAttachment/UpperLeftLabel")
|
||||
_upper_right_label = NodePath("Labels/UpperRightAttachment/UpperRightLabel")
|
||||
_lower_left_label = NodePath("Labels/LowerLeftAttachment/LowerLeftLabel")
|
||||
_lower_right_label = NodePath("Labels/LowerRightAttachment/LowerRightLabel")
|
||||
_center_label = NodePath("Labels/CenterLabel")
|
||||
_press_light = NodePath("Effects/PressLight")
|
||||
_adjacency_light = NodePath("Effects/AdjacencyLight")
|
||||
_sfx_player = NodePath("SFXPlayer")
|
||||
_press_sfx = SubResource("AudioStreamRandomizer_agrko")
|
||||
_release_sfx = SubResource("AudioStreamRandomizer_ch32x")
|
||||
|
||||
[node name="ModelPrimary" parent="." instance=ExtResource("2_6rsff")]
|
||||
[node name="Model" parent="." instance=ExtResource("16_cqjrw")]
|
||||
|
||||
[node name="KeyMesh" parent="ModelPrimary/KeyArmature/Skeleton3D" index="0"]
|
||||
gi_mode = 2
|
||||
[node name="NubMesh" parent="Model" index="0"]
|
||||
visible = false
|
||||
surface_material_override/0 = ExtResource("17_p6qfn")
|
||||
|
||||
[node name="ModelSecondary" parent="." instance=ExtResource("2_6rsff")]
|
||||
[node name="KeyMesh" parent="Model/KeyArmature/Skeleton3D" index="0"]
|
||||
surface_material_override/0 = ExtResource("17_p6qfn")
|
||||
|
||||
[node name="ModelSecondary" parent="." instance=ExtResource("16_cqjrw")]
|
||||
|
||||
[node name="NubMesh" parent="ModelSecondary" index="0"]
|
||||
visible = false
|
||||
surface_material_override/0 = ExtResource("17_p6qfn")
|
||||
|
||||
[node name="Skeleton3D" parent="ModelSecondary/KeyArmature" index="0"]
|
||||
visible = false
|
||||
|
||||
[node name="KeyMesh" parent="ModelSecondary/KeyArmature/Skeleton3D" index="0"]
|
||||
gi_mode = 2
|
||||
surface_material_override/0 = ExtResource("17_p6qfn")
|
||||
|
||||
[node name="UpperLeftAttachment" type="BoneAttachment3D" parent="."]
|
||||
[node name="Labels" type="Node3D" parent="."]
|
||||
|
||||
[node name="UpperLeftAttachment" type="BoneAttachment3D" parent="Labels"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 0, -0.5)
|
||||
bone_name = "TopLeft"
|
||||
bone_idx = 0
|
||||
use_external_skeleton = true
|
||||
external_skeleton = NodePath("../ModelPrimary/KeyArmature/Skeleton3D")
|
||||
external_skeleton = NodePath("../../Model/KeyArmature/Skeleton3D")
|
||||
|
||||
[node name="UpperLeftLabel" type="Label3D" parent="UpperLeftAttachment"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.256, 0.306, 0.264)
|
||||
[node name="UpperLeftLabel" type="Label3D" parent="Labels/UpperLeftAttachment"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.256, 0.251, 0.264)
|
||||
pixel_size = 0.006
|
||||
shaded = true
|
||||
double_sided = false
|
||||
@ -84,15 +101,15 @@ text = "Q"
|
||||
font_size = 64
|
||||
outline_size = 0
|
||||
|
||||
[node name="UpperRightAttachment" type="BoneAttachment3D" parent="."]
|
||||
[node name="UpperRightAttachment" type="BoneAttachment3D" parent="Labels"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, -0.5)
|
||||
bone_name = "TopRight"
|
||||
bone_idx = 2
|
||||
use_external_skeleton = true
|
||||
external_skeleton = NodePath("../ModelPrimary/KeyArmature/Skeleton3D")
|
||||
external_skeleton = NodePath("../../Model/KeyArmature/Skeleton3D")
|
||||
|
||||
[node name="UpperRightLabel" type="Label3D" parent="UpperRightAttachment"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.256, 0.306, 0.264)
|
||||
[node name="UpperRightLabel" type="Label3D" parent="Labels/UpperRightAttachment"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.256, 0.251, 0.264)
|
||||
pixel_size = 0.006
|
||||
shaded = true
|
||||
double_sided = false
|
||||
@ -103,15 +120,15 @@ text = "Ё"
|
||||
font_size = 64
|
||||
outline_size = 0
|
||||
|
||||
[node name="LowerLeftAttachment" type="BoneAttachment3D" parent="."]
|
||||
[node name="LowerLeftAttachment" type="BoneAttachment3D" parent="Labels"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 0, 0.5)
|
||||
bone_name = "BottomLeft"
|
||||
bone_idx = 3
|
||||
use_external_skeleton = true
|
||||
external_skeleton = NodePath("../ModelPrimary/KeyArmature/Skeleton3D")
|
||||
external_skeleton = NodePath("../../Model/KeyArmature/Skeleton3D")
|
||||
|
||||
[node name="LowerLeftLabel" type="Label3D" parent="LowerLeftAttachment"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.256, 0.306, -0.264)
|
||||
[node name="LowerLeftLabel" type="Label3D" parent="Labels/LowerLeftAttachment"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.256, 0.251, -0.264)
|
||||
pixel_size = 0.006
|
||||
shaded = true
|
||||
double_sided = false
|
||||
@ -122,15 +139,15 @@ text = "Д"
|
||||
font_size = 64
|
||||
outline_size = 0
|
||||
|
||||
[node name="LowerRightAttachment" type="BoneAttachment3D" parent="."]
|
||||
[node name="LowerRightAttachment" type="BoneAttachment3D" parent="Labels"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5)
|
||||
bone_name = "BottomRight"
|
||||
bone_idx = 1
|
||||
use_external_skeleton = true
|
||||
external_skeleton = NodePath("../ModelPrimary/KeyArmature/Skeleton3D")
|
||||
external_skeleton = NodePath("../../Model/KeyArmature/Skeleton3D")
|
||||
|
||||
[node name="LowerRightLabel" type="Label3D" parent="LowerRightAttachment"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.256, 0.306, -0.264)
|
||||
[node name="LowerRightLabel" type="Label3D" parent="Labels/LowerRightAttachment"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.256, 0.251, -0.264)
|
||||
pixel_size = 0.006
|
||||
shaded = true
|
||||
double_sided = false
|
||||
@ -141,8 +158,8 @@ text = ","
|
||||
font_size = 64
|
||||
outline_size = 0
|
||||
|
||||
[node name="CenterLabel" type="Label3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.305619, 0)
|
||||
[node name="CenterLabel" type="Label3D" parent="Labels"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.251, 0)
|
||||
pixel_size = 0.006
|
||||
shaded = true
|
||||
double_sided = false
|
||||
@ -153,18 +170,30 @@ text = "A"
|
||||
font_size = 48
|
||||
outline_size = 0
|
||||
|
||||
[node name="PressLight" type="OmniLight3D" parent="."]
|
||||
[node name="Effects" type="Node3D" parent="."]
|
||||
|
||||
[node name="PressLight" type="OmniLight3D" parent="Effects"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
|
||||
visible = false
|
||||
light_color = Color(1, 0.687333, 0.33, 1)
|
||||
light_energy = 6.0
|
||||
omni_range = 2.0
|
||||
|
||||
[node name="AdjacencyLight" type="OmniLight3D" parent="Effects"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.45, 0)
|
||||
visible = false
|
||||
light_color = Color(0.8325, 1, 0.33, 1)
|
||||
omni_range = 0.6
|
||||
|
||||
[node name="PlayerPosMarker" type="Marker3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, 0)
|
||||
gizmo_extents = 0.2
|
||||
|
||||
[node name="SFXPlayer" type="AudioStreamPlayer3D" parent="."]
|
||||
stream = SubResource("AudioStreamPolyphonic_6rsff")
|
||||
volume_db = -15.0
|
||||
autoplay = true
|
||||
attenuation_model = 3
|
||||
bus = &"SFX"
|
||||
attenuation_filter_cutoff_hz = 20500.0
|
||||
|
||||
[editable path="ModelPrimary"]
|
||||
[editable path="Model"]
|
||||
[editable path="ModelSecondary"]
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://dl5jgx2ucmvk7"]
|
||||
[gd_scene load_steps=8 format=3 uid="uid://dl5jgx2ucmvk7"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c7ikemicshkhv" path="res://scripts/game_keyboard.gd" id="1_3k4ps"]
|
||||
[ext_resource type="PackedScene" uid="uid://bryima34hc3yp" path="res://scenes/game_key.tscn" id="2_hylia"]
|
||||
[ext_resource type="AudioStream" uid="uid://db7pm7d6amnpk" path="res://assets/sfx/keyboard/swap.wav" id="2_j5t22"]
|
||||
|
||||
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_hylia"]
|
||||
@ -10,17 +11,33 @@ stream_0/stream = ExtResource("2_j5t22")
|
||||
|
||||
[sub_resource type="AudioStreamPolyphonic" id="AudioStreamPolyphonic_3k4ps"]
|
||||
|
||||
[node name="GameKeyboard" type="Node3D" node_paths=PackedStringArray("_keys_holder", "_sfx_player")]
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_j5t22"]
|
||||
size = Vector3(0.09, 2.075, 0.09)
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_j5t22"]
|
||||
orientation = 1
|
||||
|
||||
[node name="GameKeyboard" type="Node3D" node_paths=PackedStringArray("_keys_holder", "_sfx_player", "_rect_mesh")]
|
||||
script = ExtResource("1_3k4ps")
|
||||
_keys_holder = NodePath("Keys")
|
||||
_sfx_player = NodePath("SFXPlayer")
|
||||
_rect_mesh = NodePath("RectMesh")
|
||||
_key_scene = ExtResource("2_hylia")
|
||||
_layout_swap_sfx = SubResource("AudioStreamRandomizer_hylia")
|
||||
|
||||
[node name="Keys" type="Node3D" parent="."]
|
||||
|
||||
[node name="SFXPlayer" type="AudioStreamPlayer3D" parent="."]
|
||||
stream = SubResource("AudioStreamPolyphonic_3k4ps")
|
||||
volume_db = -15.0
|
||||
autoplay = true
|
||||
attenuation_model = 3
|
||||
bus = &"SFX"
|
||||
attenuation_filter_cutoff_hz = 20500.0
|
||||
|
||||
[node name="CenterMesh" type="MeshInstance3D" parent="."]
|
||||
visible = false
|
||||
mesh = SubResource("BoxMesh_j5t22")
|
||||
|
||||
[node name="RectMesh" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(16.4, 0, 0, 0, 1, 0, 0, 0, 5.4, 0, -0.968737, 0)
|
||||
visible = false
|
||||
mesh = SubResource("QuadMesh_j5t22")
|
||||
|
||||
43
scenes/player.tscn
Normal file
43
scenes/player.tscn
Normal file
@ -0,0 +1,43 @@
|
||||
[gd_scene load_steps=10 format=3 uid="uid://b8gu0udt0ow8t"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dmfdlb4ae57qv" path="res://scripts/player.gd" id="1_3vyb7"]
|
||||
[ext_resource type="AudioStream" uid="uid://86cbncjjh15f" path="res://assets/sfx/player/move/move_1.wav" id="2_qhqgy"]
|
||||
[ext_resource type="AudioStream" uid="uid://daqhk1ae8oocx" path="res://assets/sfx/player/move/move_2.wav" id="3_dqkch"]
|
||||
[ext_resource type="AudioStream" uid="uid://c02pnx1fm8bqa" path="res://assets/sfx/player/move/move_3.wav" id="4_qlg0r"]
|
||||
[ext_resource type="AudioStream" uid="uid://dmoypbjc535kp" path="res://assets/sfx/player/move/move_4.wav" id="5_tuyoq"]
|
||||
|
||||
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_qlg0r"]
|
||||
random_pitch = 1.15
|
||||
streams_count = 4
|
||||
stream_0/stream = ExtResource("2_qhqgy")
|
||||
stream_1/stream = ExtResource("3_dqkch")
|
||||
stream_2/stream = ExtResource("4_qlg0r")
|
||||
stream_3/stream = ExtResource("5_tuyoq")
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3vyb7"]
|
||||
metallic_specular = 0.0
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_u8vuu"]
|
||||
material = SubResource("StandardMaterial3D_3vyb7")
|
||||
size = Vector3(0.6, 0.6, 0.6)
|
||||
|
||||
[sub_resource type="AudioStreamPolyphonic" id="AudioStreamPolyphonic_3vyb7"]
|
||||
|
||||
[node name="Player" type="Node3D" node_paths=PackedStringArray("_sfx_player")]
|
||||
script = ExtResource("1_3vyb7")
|
||||
_sfx_player = NodePath("SFXPlayer")
|
||||
_hop_sfx = SubResource("AudioStreamRandomizer_qlg0r")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0)
|
||||
mesh = SubResource("BoxMesh_u8vuu")
|
||||
|
||||
[node name="MeshInstance3D2" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(0.310767, 0, 0, 0, 0.310767, 0, 0, 0, 0.310767, 0, 0.394854, 0.356113)
|
||||
mesh = SubResource("BoxMesh_u8vuu")
|
||||
|
||||
[node name="SFXPlayer" type="AudioStreamPlayer3D" parent="."]
|
||||
stream = SubResource("AudioStreamPolyphonic_3vyb7")
|
||||
attenuation_model = 3
|
||||
bus = &"SFX"
|
||||
attenuation_filter_cutoff_hz = 20500.0
|
||||
@ -1,7 +1,8 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://djmj08xtpgpgk"]
|
||||
[gd_scene load_steps=7 format=3 uid="uid://djmj08xtpgpgk"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://dl5jgx2ucmvk7" path="res://scenes/game_keyboard.tscn" id="1_errlg"]
|
||||
[ext_resource type="PackedScene" uid="uid://df16rl6w7vvw" path="res://scenes/debug_key_display.tscn" id="2_bl13t"]
|
||||
[ext_resource type="PackedScene" uid="uid://dl5jgx2ucmvk7" path="res://scenes/game_keyboard.tscn" id="1_da6vm"]
|
||||
[ext_resource type="PackedScene" uid="uid://b8gu0udt0ow8t" path="res://scenes/player.tscn" id="2_51e2l"]
|
||||
[ext_resource type="PackedScene" uid="uid://df16rl6w7vvw" path="res://scenes/debug_key_display.tscn" id="2_st4qh"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_errlg"]
|
||||
albedo_color = Color(0.2, 0.2, 0.2, 1)
|
||||
@ -22,13 +23,14 @@ glow_strength = 1.5
|
||||
[node name="Test" type="Node3D"]
|
||||
|
||||
[node name="OrthogonalCamera" type="Camera3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 19.632, -0.848)
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 19.632, 0)
|
||||
projection = 1
|
||||
size = 11.58
|
||||
size = 14.532
|
||||
|
||||
[node name="PerspectiveCamera" type="Camera3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.48725, 0.873262, 0, -0.873262, 0.48725, 0, 11.172, 3.456)
|
||||
transform = Transform3D(1, 0, 0, 0, 0.642787, 0.766044, 0, -0.766044, 0.642787, 0, 12.5, 8)
|
||||
current = true
|
||||
fov = 60.0
|
||||
|
||||
[node name="FloorMesh" type="MeshInstance3D" parent="."]
|
||||
visible = false
|
||||
@ -40,8 +42,10 @@ transform = Transform3D(0.312269, -0.815913, 0.486594, -2.95249e-09, 0.512208, 0
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_ij1v8")
|
||||
|
||||
[node name="GameKeyboard" parent="." instance=ExtResource("1_errlg")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0721761, 0)
|
||||
[node name="GameKeyboard" parent="." instance=ExtResource("1_da6vm")]
|
||||
|
||||
[node name="DebugKeyDisplay" parent="." node_paths=PackedStringArray("_keyboard") instance=ExtResource("2_bl13t")]
|
||||
[node name="Player" parent="." node_paths=PackedStringArray("_keyboard") instance=ExtResource("2_51e2l")]
|
||||
_keyboard = NodePath("../GameKeyboard")
|
||||
|
||||
[node name="DebugKeyDisplay" parent="." node_paths=PackedStringArray("_keyboard") instance=ExtResource("2_st4qh")]
|
||||
_keyboard = NodePath("../GameKeyboard")
|
||||
@ -9,13 +9,19 @@ extends Control
|
||||
|
||||
func _ready() -> void:
|
||||
_set_layout_label(LayoutConfig.current_layout)
|
||||
_set_layout_swap_label()
|
||||
_set_layout_swap_label(_keyboard.prompt_page)
|
||||
_input_label.text = "awaiting input..."
|
||||
_typing_label.text = ""
|
||||
_keyboard.key_press_changed.connect(_on_keyboard_key_press_changed)
|
||||
_keyboard.key_input_event_emitted.connect(_on_keyboard_key_input_event_emitted)
|
||||
_keyboard.prompt_page_turned.connect(_on_keyboard_prompt_page_turned)
|
||||
LayoutConfig.layout_swapped.connect(_set_layout_label)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
_layout_label.visible = _keyboard.is_configuring
|
||||
_layout_swap_label.visible = _keyboard.is_configuring
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is not InputEventKey or not event.is_pressed():
|
||||
return
|
||||
@ -35,7 +41,9 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
text += "as_text_location: %s\n" % event_key.as_text_location()
|
||||
text += "as_text_physical_keycode: %s\n" % event_key.as_text_physical_keycode()
|
||||
text += "\n"
|
||||
text += "get_key_label_with_modifiers: %s\n" % event_key.get_key_label_with_modifiers()
|
||||
text += (
|
||||
"get_key_label_with_modifiers: %s\n" % event_key.get_key_label_with_modifiers()
|
||||
)
|
||||
text += "get_keycode_with_modifiers: %s\n" % event_key.get_keycode_with_modifiers()
|
||||
text += (
|
||||
"get_physical_keycode_with_modifiers: %s\n"
|
||||
@ -46,20 +54,24 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
|
||||
|
||||
func _set_layout_label(layout: AbstractLayout) -> void:
|
||||
_layout_label.text = "physical layout: %s" % layout.name()
|
||||
_layout_label.text = "physical layout: %s" % layout.get_name()
|
||||
|
||||
|
||||
func _set_layout_swap_label() -> void:
|
||||
var text := ""
|
||||
func _set_layout_swap_label(page: int) -> void:
|
||||
var text := (
|
||||
"page %s/%s Kp 0: next page >>>\n"
|
||||
% [_keyboard.prompt_page + 1, _keyboard.prompt_pages_total]
|
||||
)
|
||||
|
||||
var layouts := LayoutConfig.layouts.values() as Array[AbstractLayout]
|
||||
for i in layouts.size():
|
||||
var key := (KEY_KP_1 + i) as Key
|
||||
var key: int = KEY_KP_1
|
||||
for i in range(page * 9, mini(layouts.size(), page * 9 + 9)):
|
||||
var input_event := InputEventKey.new()
|
||||
input_event.physical_keycode = key
|
||||
input_event.physical_keycode = key as Key
|
||||
text += (
|
||||
"%s: %s\n" % [input_event.as_text_physical_keycode(), layouts[i].name()]
|
||||
"%s: %s\n" % [input_event.as_text_physical_keycode(), layouts[i].get_name()]
|
||||
)
|
||||
key += 1
|
||||
|
||||
_layout_swap_label.text = text
|
||||
|
||||
@ -75,13 +87,19 @@ func _erase_typing_label() -> void:
|
||||
_typing_label.text = _typing_label.text.substr(0, _typing_label.text.length() - 1)
|
||||
|
||||
|
||||
func _on_keyboard_key_press_changed(game_key: GameKey, event: InputEventKey) -> void:
|
||||
func _on_keyboard_key_input_event_emitted(
|
||||
game_key: GameKey, event: InputEventKey
|
||||
) -> void:
|
||||
if not event.is_pressed():
|
||||
return
|
||||
|
||||
if game_key.props.is_char():
|
||||
if game_key.props.is_unicode():
|
||||
_set_typing_label(char(event.unicode))
|
||||
elif game_key.props.physical_keycode == KEY_SPACE:
|
||||
elif game_key.props.keycode == KEY_SPACE:
|
||||
_set_typing_label(" ")
|
||||
elif game_key.props.physical_keycode == KEY_BACKSPACE:
|
||||
elif game_key.props.keycode == KEY_BACKSPACE:
|
||||
_erase_typing_label()
|
||||
|
||||
|
||||
func _on_keyboard_prompt_page_turned(page: int) -> void:
|
||||
_set_layout_swap_label(page)
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
@tool
|
||||
class_name GameKey extends Node3D
|
||||
|
||||
#region variables
|
||||
static var _scene := preload("res://scenes/game_key.tscn")
|
||||
@export_group("References")
|
||||
@export var player_pos_marker: Node3D
|
||||
|
||||
@export_group("Node references")
|
||||
@export var _skeleton_primary: Skeleton3D
|
||||
@export var _skeleton_secondary: Skeleton3D
|
||||
@export var _nub_mesh: MeshInstance3D
|
||||
@export var _upper_left_label: Label3D
|
||||
@export var _upper_right_label: Label3D
|
||||
@export var _lower_left_label: Label3D
|
||||
@export var _lower_right_label: Label3D
|
||||
@export var _center_label: Label3D
|
||||
@export var _press_light: OmniLight3D
|
||||
@export var _adjacency_light: OmniLight3D
|
||||
@export var _sfx_player: AudioStreamPlayer3D
|
||||
|
||||
@export_group("Bones")
|
||||
@ -24,51 +27,62 @@ static var _scene := preload("res://scenes/game_key.tscn")
|
||||
|
||||
@export_group("Animation")
|
||||
@export var _pos_sod_fzr: Vector3 = Vector3(5, 0.15, 0)
|
||||
@export var _light_fade_time: float = 0.25
|
||||
@export var _star_pop_offset: Vector3 = Vector3(0, -2, 0)
|
||||
@export var _light_fade_duration: float = 0.25
|
||||
@export var _starting_pop_offset: Vector3 = Vector3(0, -2.5, 0)
|
||||
@export var _rotation_damping: float = 10
|
||||
|
||||
@export_subgroup("Idle")
|
||||
@export var _idle_amplitude: float = 0.4
|
||||
@export var _idle_frequency := Vector2(0.25, -0.25)
|
||||
@export var _press_offset: float = 0.4
|
||||
|
||||
@export_subgroup("Player")
|
||||
@export var _hop_land_offset: float = 0.8
|
||||
|
||||
@export_group("SFX")
|
||||
@export var _press_sfx: AudioStream
|
||||
@export var _release_sfx: AudioStream
|
||||
|
||||
var props: KeyProps
|
||||
var keyboard: GameKeyboard
|
||||
|
||||
var player_pos_marker_transform: Transform3D
|
||||
|
||||
var _keyboard: GameKeyboard
|
||||
var _is_pressed: bool
|
||||
|
||||
var _light_timer: float
|
||||
var _default_position: Vector3
|
||||
var _init_pos: Vector3
|
||||
var _default_pos: Vector3
|
||||
var _default_rot: Vector3
|
||||
var _pos_sod: SecondOrderDynamics
|
||||
|
||||
@onready
|
||||
var _polyphonic := _sfx_player.get_stream_playback() as AudioStreamPlaybackPolyphonic
|
||||
var _polyphonic: AudioStreamPlaybackPolyphonic
|
||||
|
||||
@onready var _light_energy := _press_light.light_energy
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region static methods
|
||||
#region static
|
||||
static func instantiate_with_props(
|
||||
_props: KeyProps, default_position: Vector3, _keyboard: GameKeyboard
|
||||
_props: KeyProps,
|
||||
init_pos: Vector3,
|
||||
angle: float,
|
||||
keyboard: GameKeyboard,
|
||||
scene: PackedScene
|
||||
) -> GameKey:
|
||||
var node := _scene.instantiate() as GameKey
|
||||
node.keyboard = _keyboard
|
||||
var game_key := scene.instantiate() as GameKey
|
||||
game_key._keyboard = keyboard
|
||||
game_key.load_props(_props, init_pos, angle)
|
||||
|
||||
if _props.physical_keycode != KEY_SPECIAL:
|
||||
node.name = node.name + " " + OS.get_keycode_string(_props.physical_keycode)
|
||||
if _props.location != KEY_LOCATION_UNSPECIFIED:
|
||||
node.name += (
|
||||
" " + ("Left" if _props.location == KEY_LOCATION_LEFT else "Right")
|
||||
)
|
||||
if _props.keycode != KEY_NONE:
|
||||
game_key.name += " " + OS.get_keycode_string(_props.keycode)
|
||||
if _props.location != KEY_LOCATION_UNSPECIFIED:
|
||||
game_key.name += (
|
||||
" " + ("Left" if _props.location == KEY_LOCATION_LEFT else "Right")
|
||||
)
|
||||
|
||||
node.load_props(_props, default_position)
|
||||
|
||||
return node
|
||||
return game_key
|
||||
|
||||
|
||||
#endregion
|
||||
@ -76,50 +90,72 @@ static func instantiate_with_props(
|
||||
|
||||
#region builtins
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint() and not _keyboard:
|
||||
return
|
||||
_adjacency_light.visible = false
|
||||
_keyboard.layout_changed.connect(_on_keyboard_layout_changed)
|
||||
_keyboard.keys_queried.connect(_on_keyboard_keys_queried)
|
||||
_keyboard.configuring_changed.connect(_on_keyboard_configuring_changed)
|
||||
_keyboard.player_current_key_changed.connect(
|
||||
_on_keyboard_player_current_key_changed
|
||||
)
|
||||
_keyboard.player_finished_move.connect(_on_keyboard_player_finished_move)
|
||||
_set_labels()
|
||||
_reset_animations()
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
_sfx_player.play()
|
||||
_polyphonic = _sfx_player.get_stream_playback()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
_animate(delta)
|
||||
_animate_light(delta)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
if event.is_action_pressed("reset_animations"):
|
||||
_reset_animations()
|
||||
return
|
||||
|
||||
if event is not InputEventKey:
|
||||
return
|
||||
|
||||
var event_key := event as InputEventKey
|
||||
|
||||
if (
|
||||
event_key.physical_keycode != props.physical_keycode
|
||||
or event_key.location != props.location
|
||||
event_key.physical_keycode != props.keycode
|
||||
or event_key.echo
|
||||
or event_key.physical_keycode == KEY_NONE
|
||||
or props.keycode == KEY_NONE
|
||||
or (
|
||||
event_key.location != KEY_LOCATION_UNSPECIFIED
|
||||
and props.location != KEY_LOCATION_UNSPECIFIED
|
||||
and event_key.location != props.location
|
||||
)
|
||||
):
|
||||
return
|
||||
|
||||
_set_pressing_from_event(event_key)
|
||||
_set_pressing(event_key.is_pressed())
|
||||
_keyboard.emit_key_input_event_emitted(self, event_key)
|
||||
|
||||
if event_key.echo:
|
||||
return
|
||||
|
||||
if LayoutConfig.is_configuring and event_key.is_pressed():
|
||||
var new_char_type: KeyProps.Char
|
||||
if not keyboard.alt_visual_layout:
|
||||
if _keyboard.is_configuring and event_key.is_pressed():
|
||||
var new_char_type: String
|
||||
if not _keyboard.alt_visual_layout:
|
||||
if not event_key.shift_pressed:
|
||||
new_char_type = KeyProps.Char.MAIN
|
||||
new_char_type = KeyProps.CHAR_MAIN
|
||||
else:
|
||||
new_char_type = KeyProps.Char.SHIFT
|
||||
new_char_type = KeyProps.CHAR_SHIFT
|
||||
else:
|
||||
if not event_key.shift_pressed:
|
||||
new_char_type = KeyProps.Char.ALT
|
||||
new_char_type = KeyProps.CHAR_ALT
|
||||
else:
|
||||
new_char_type = KeyProps.Char.ALT_SHIFT
|
||||
new_char_type = KeyProps.CHAR_ALT_SHIFT
|
||||
|
||||
var chars_dict: Dictionary[KeyProps.Char, String] = {
|
||||
var chars_dict: Dictionary[String, String] = {
|
||||
new_char_type: char(event_key.unicode).to_upper()
|
||||
}
|
||||
|
||||
@ -127,60 +163,66 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
_set_keyboard_pressed_position(false)
|
||||
if _keyboard:
|
||||
_erase_keyboard_pressed_position()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region public
|
||||
func load_props(_props: KeyProps, default_position: Vector3) -> void:
|
||||
_set_keyboard_pressed_position(false)
|
||||
_default_position = default_position
|
||||
if _is_pressed:
|
||||
_set_keyboard_pressed_position(true)
|
||||
func load_props(_props: KeyProps, init_pos: Vector3, angle: float) -> void:
|
||||
_init_pos = init_pos
|
||||
_default_rot.y = angle
|
||||
|
||||
if props:
|
||||
_props.chars_from_dict(props.chars_to_dict())
|
||||
props = _props
|
||||
|
||||
_skeleton_primary.visible = true
|
||||
_skeleton_secondary.visible = props.secondary_rect
|
||||
_skeleton_secondary.visible = props.has_secondary_rect()
|
||||
_set_bones()
|
||||
_nub_mesh.visible = props.homing_nub
|
||||
|
||||
|
||||
func get_default_transform() -> Transform3D:
|
||||
return Transform3D(Quaternion.from_euler(_default_rot), _default_pos)
|
||||
|
||||
|
||||
func is_adjacent(to: GameKey) -> bool:
|
||||
return (
|
||||
KeyHelper.ADJACENCY_MAP.has(props.keycode)
|
||||
and (to.props.keycode in KeyHelper.ADJACENCY_MAP[props.keycode])
|
||||
)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region pressing
|
||||
func _set_pressing_from_event(event: InputEventKey) -> void:
|
||||
_set_pressing(event.is_pressed())
|
||||
keyboard.emit_key_press(self, event)
|
||||
|
||||
|
||||
func _set_pressing(is_pressed: bool) -> void:
|
||||
if _is_pressed == is_pressed:
|
||||
return
|
||||
_is_pressed = is_pressed
|
||||
_set_keyboard_pressed_position(_is_pressed)
|
||||
if _is_pressed:
|
||||
_play_sfx(_press_sfx)
|
||||
else:
|
||||
_play_sfx(_release_sfx)
|
||||
_keyboard.emit_key_pressed(self)
|
||||
_play_sfx(_press_sfx if _is_pressed else _release_sfx)
|
||||
|
||||
|
||||
func _set_keyboard_pressed_position(pressed: bool) -> void:
|
||||
if pressed:
|
||||
keyboard.pressed_positions[_default_position] = pressed
|
||||
else:
|
||||
keyboard.pressed_positions.erase(_default_position)
|
||||
_keyboard.pressed_positions[_default_pos] = pressed
|
||||
|
||||
|
||||
func _erase_keyboard_pressed_position() -> void:
|
||||
_keyboard.pressed_positions.erase(_default_pos)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region labels
|
||||
func _set_labels(chars_dict: Dictionary[KeyProps.Char, String] = {}) -> void:
|
||||
func _set_labels(chars_dict: Dictionary[String, String] = {}) -> void:
|
||||
_upper_left_label.text = ""
|
||||
_upper_right_label.text = ""
|
||||
_lower_left_label.text = ""
|
||||
@ -196,12 +238,9 @@ func _set_labels(chars_dict: Dictionary[KeyProps.Char, String] = {}) -> void:
|
||||
_center_label.visible = _center_label.text != ""
|
||||
|
||||
|
||||
func _set_labels_text(chars_dict: Dictionary[KeyProps.Char, String]) -> void:
|
||||
if props.physical_keycode == KEY_SPECIAL:
|
||||
return
|
||||
|
||||
if not props.is_char():
|
||||
_center_label.text = OS.get_keycode_string(props.physical_keycode)
|
||||
func _set_labels_text(chars_dict: Dictionary[String, String]) -> void:
|
||||
if Engine.is_editor_hint() or not props.is_unicode():
|
||||
_center_label.text = OS.get_keycode_string(props.keycode)
|
||||
return
|
||||
|
||||
if chars_dict:
|
||||
@ -230,20 +269,24 @@ func _set_labels_text(chars_dict: Dictionary[KeyProps.Char, String]) -> void:
|
||||
#region bones
|
||||
func _set_bones() -> void:
|
||||
var rect := Rect2(
|
||||
-(keyboard.key_size * props.width) / 2,
|
||||
-keyboard.key_size / 2,
|
||||
keyboard.key_size * props.width,
|
||||
keyboard.key_size * props.height
|
||||
-(_keyboard.key_size * props.width) / 2,
|
||||
-_keyboard.key_size / 2,
|
||||
_keyboard.key_size * props.width,
|
||||
_keyboard.key_size * props.height
|
||||
)
|
||||
|
||||
var rect_center := rect.get_center()
|
||||
_center_label.position.x = rect_center.x
|
||||
_center_label.position.z = rect_center.y
|
||||
|
||||
_set_bones_from_rect(rect, _skeleton_primary)
|
||||
|
||||
if props.secondary_rect:
|
||||
if props.has_secondary_rect():
|
||||
var rect_secondary := Rect2(
|
||||
rect.position.x + props.x2 * keyboard.key_size,
|
||||
rect.position.y + props.y2 * keyboard.key_size,
|
||||
keyboard.key_size * props.width2,
|
||||
keyboard.key_size * props.height2
|
||||
rect.position.x + props.x2 * _keyboard.key_size,
|
||||
rect.position.y + props.y2 * _keyboard.key_size,
|
||||
_keyboard.key_size * props.width2,
|
||||
_keyboard.key_size * props.height2
|
||||
)
|
||||
|
||||
_set_bones_from_rect(rect_secondary, _skeleton_secondary)
|
||||
@ -277,44 +320,69 @@ func _set_bones_from_rect(rect: Rect2, skeleton: Skeleton3D) -> void:
|
||||
#region animation
|
||||
func _reset_animations() -> void:
|
||||
_pos_sod = SecondOrderDynamics.new(
|
||||
_pos_sod_fzr, _default_position + _star_pop_offset
|
||||
_pos_sod_fzr, _default_pos + _starting_pop_offset
|
||||
)
|
||||
|
||||
|
||||
func _animate(delta: float) -> void:
|
||||
position = _pos_sod.process(delta, _animate_position())
|
||||
rotation = lerp(rotation, _default_rot, delta * _rotation_damping)
|
||||
|
||||
|
||||
func _animate_position() -> Vector3:
|
||||
var new_position := _default_position
|
||||
var new_position := _default_pos
|
||||
|
||||
new_position.y += (
|
||||
sin(
|
||||
(
|
||||
keyboard.anim_time
|
||||
+ (_default_position.x / keyboard.key_size) * _idle_frequency.x
|
||||
+ (_default_position.z / keyboard.key_size) * _idle_frequency.y
|
||||
_keyboard.anim_time
|
||||
+ (_default_pos.x / _keyboard.key_size) * _idle_frequency.x
|
||||
+ (_default_pos.z / _keyboard.key_size) * _idle_frequency.y
|
||||
)
|
||||
)
|
||||
* _idle_amplitude
|
||||
* keyboard.key_size
|
||||
* _keyboard.key_size
|
||||
)
|
||||
|
||||
new_position.y -= (_press_offset * keyboard.key_size) if _is_pressed else 0.0
|
||||
new_position.y -= (_press_offset * _keyboard.key_size) if _is_pressed else 0.0
|
||||
|
||||
return new_position
|
||||
|
||||
|
||||
func _anim_shake(force: float) -> void:
|
||||
_pos_sod.y = (
|
||||
_pos_sod.y
|
||||
+ (
|
||||
Vector3(
|
||||
randf_range(-force, force),
|
||||
randf_range(-force, force),
|
||||
randf_range(-force, force)
|
||||
)
|
||||
* _keyboard.key_size
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func _anim_push(force: Vector3) -> void:
|
||||
_pos_sod.y = (_pos_sod.y + force * _keyboard.key_size)
|
||||
|
||||
|
||||
func _anim_push_radial(force: float) -> void:
|
||||
_pos_sod.y = (Vector3(
|
||||
_pos_sod.y.x * (force + 1), _pos_sod.y.y, _pos_sod.y.z * (force + 1)
|
||||
))
|
||||
|
||||
|
||||
func _animate_light(delta: float) -> void:
|
||||
if _is_pressed:
|
||||
_light_timer = _light_fade_time
|
||||
_light_timer = _light_fade_duration
|
||||
|
||||
if _light_timer <= 0:
|
||||
_press_light.visible = false
|
||||
return
|
||||
|
||||
_press_light.visible = true
|
||||
_press_light.light_energy = (_light_timer / _light_fade_time) * _light_energy
|
||||
_press_light.light_energy = (_light_timer / _light_fade_duration) * _light_energy
|
||||
_light_timer -= delta
|
||||
|
||||
|
||||
@ -325,4 +393,46 @@ func _animate_light(delta: float) -> void:
|
||||
func _play_sfx(stream: AudioStream) -> void:
|
||||
_polyphonic.play_stream(stream)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region event handlers
|
||||
func _on_keyboard_layout_changed(rect: Rect2) -> void:
|
||||
_erase_keyboard_pressed_position()
|
||||
var center := rect.get_center()
|
||||
_default_pos = _init_pos
|
||||
_default_pos.x -= center.x
|
||||
_default_pos.z -= center.y
|
||||
_set_keyboard_pressed_position(_is_pressed)
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
position = _default_pos
|
||||
return
|
||||
|
||||
if not _pos_sod:
|
||||
_reset_animations()
|
||||
|
||||
|
||||
func _on_keyboard_keys_queried(query_func: Callable) -> void:
|
||||
if query_func.call(self):
|
||||
_keyboard.key_query_respond(self)
|
||||
|
||||
|
||||
func _on_keyboard_configuring_changed(is_configuring: bool) -> void:
|
||||
if is_configuring:
|
||||
_anim_push_radial(0.2)
|
||||
else:
|
||||
_anim_shake(0.2)
|
||||
_anim_push_radial(-0.05)
|
||||
|
||||
|
||||
func _on_keyboard_player_current_key_changed(game_key: GameKey) -> void:
|
||||
_adjacency_light.visible = is_adjacent(game_key)
|
||||
|
||||
|
||||
func _on_keyboard_player_finished_move(game_key: GameKey) -> void:
|
||||
if game_key == self:
|
||||
_anim_push(Vector3.DOWN * _hop_land_offset)
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1,79 +1,113 @@
|
||||
@tool
|
||||
class_name GameKeyboard extends Node3D
|
||||
|
||||
signal key_press_changed(game_key: GameKey, event: InputEventKey)
|
||||
|
||||
#region variables
|
||||
@export_group("Node references")
|
||||
signal key_pressed(game_key: GameKey)
|
||||
signal key_input_event_emitted(game_key: GameKey, event: InputEventKey)
|
||||
signal layout_changed(rect: Rect2)
|
||||
signal prompt_page_turned(page: int)
|
||||
signal keys_queried(query_func: Callable)
|
||||
signal configuring_changed(is_configuring: bool)
|
||||
signal player_current_key_changed(game_key: GameKey)
|
||||
signal player_finished_move(game_key: GameKey)
|
||||
|
||||
@export_tool_button("Generate keys")
|
||||
var generate_editor_keys_btn := _generate_editor_keys
|
||||
@export_tool_button("Delete keys") var delete_editor_keys_btn := _delete_editor_keys
|
||||
@export_tool_button("Request keys") var query_keys_btn := _query_editor_keys
|
||||
|
||||
@export_group("References")
|
||||
@export var _keys_holder: Node3D
|
||||
@export var _sfx_player: AudioStreamPlayer3D
|
||||
@export var _rect_mesh: MeshInstance3D
|
||||
@export var _key_scene: PackedScene
|
||||
|
||||
@export_group("Key params")
|
||||
@export_group("Key parameters")
|
||||
@export var key_size: float = 1
|
||||
@export var key_gap: float = 0.1
|
||||
|
||||
@export_group("Animation")
|
||||
@export var _rot_sod_fzr: Vector3 = Vector3(3, 0.1, 2)
|
||||
@export var _time_scale: float = 0.5
|
||||
@export var _anim_time_scale: float = 0.5
|
||||
@export var _pressing_lean_deg := Vector2(0.15, 0.7)
|
||||
|
||||
@export_group("SFX")
|
||||
@export var _layout_swap_sfx: AudioStream
|
||||
|
||||
var is_configuring: bool
|
||||
var alt_visual_layout: bool
|
||||
var layout_rect: Rect2
|
||||
var pressed_positions: Dictionary[Vector3, bool]
|
||||
var anim_time: float
|
||||
|
||||
var prompt_page: int = 0
|
||||
var prompt_pages_total: int = 0
|
||||
|
||||
var _rot_sod: SecondOrderDynamics
|
||||
|
||||
var _queried_keys: Array[GameKey] = []
|
||||
var _queried_keys_limit: int = 0
|
||||
|
||||
var _polyphonic: AudioStreamPlaybackPolyphonic
|
||||
|
||||
@onready var _gap_to_size_ratio: float
|
||||
@onready var _pressing_lean_rad := Vector2(
|
||||
deg_to_rad(_pressing_lean_deg.x), deg_to_rad(_pressing_lean_deg.y)
|
||||
)
|
||||
@onready var _default_rotation: Vector3 = rotation
|
||||
@onready
|
||||
var _polyphonic := _sfx_player.get_stream_playback() as AudioStreamPlaybackPolyphonic
|
||||
@onready var _default_rot: Vector3 = _keys_holder.rotation
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region builtins
|
||||
func _ready() -> void:
|
||||
_generate_keys(LayoutConfig.layout_rows)
|
||||
if Engine.is_editor_hint():
|
||||
_generate_editor_keys()
|
||||
return
|
||||
prompt_pages_total = ceili(LayoutConfig.layouts.size() / 9.0)
|
||||
_generate_game_keys()
|
||||
_reset_animations()
|
||||
_sfx_player.play()
|
||||
_polyphonic = _sfx_player.get_stream_playback()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
_animate(delta)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
if event.is_action_pressed("reset_animations"):
|
||||
_reset_animations()
|
||||
return
|
||||
if event.is_action_pressed("toggle_configuring"):
|
||||
is_configuring = not is_configuring
|
||||
print("now configuring: %s" % is_configuring)
|
||||
configuring_changed.emit(is_configuring)
|
||||
return
|
||||
|
||||
if event is not InputEventKey:
|
||||
return
|
||||
|
||||
var event_key := event as InputEventKey
|
||||
|
||||
if event_key.echo:
|
||||
if event_key.echo or not event_key.is_pressed():
|
||||
return
|
||||
|
||||
if (
|
||||
event_key.is_pressed()
|
||||
and LayoutConfig.is_configuring
|
||||
is_configuring
|
||||
and event_key.physical_keycode >= KEY_KP_0
|
||||
and event_key.physical_keycode <= KEY_KP_9
|
||||
):
|
||||
_swap_layout(event_key.physical_keycode)
|
||||
_change_layout(event_key.physical_keycode)
|
||||
return
|
||||
|
||||
var keycode := event_key.get_physical_keycode_with_modifiers()
|
||||
|
||||
if (
|
||||
event_key.is_pressed()
|
||||
and (keycode == KEY_SHIFT | KEY_MASK_ALT or keycode == KEY_ALT | KEY_MASK_SHIFT)
|
||||
):
|
||||
if keycode == KEY_SHIFT | KEY_MASK_ALT or keycode == KEY_ALT | KEY_MASK_SHIFT:
|
||||
alt_visual_layout = not alt_visual_layout
|
||||
print("changed visual layout! " + str(alt_visual_layout))
|
||||
|
||||
@ -82,54 +116,99 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
|
||||
|
||||
#region public
|
||||
func emit_key_press(game_key: GameKey, event: InputEventKey) -> void:
|
||||
key_press_changed.emit(game_key, event)
|
||||
func query_keys(query_func: Callable, limit: int = 0) -> Array[GameKey]:
|
||||
_queried_keys = []
|
||||
_queried_keys_limit = limit
|
||||
|
||||
keys_queried.emit(query_func)
|
||||
var queried_keys := _queried_keys
|
||||
|
||||
_queried_keys = []
|
||||
_queried_keys_limit = 0
|
||||
|
||||
queried_keys.sort_custom(KeyHelper.game_key_sort)
|
||||
return queried_keys
|
||||
|
||||
|
||||
func query_key_by_keycode(keycode: Key) -> GameKey:
|
||||
var found_key: GameKey
|
||||
var result := query_keys(
|
||||
func(game_key: GameKey) -> bool: return game_key.props.keycode == keycode, 1
|
||||
)
|
||||
if result:
|
||||
found_key = result[0]
|
||||
return found_key
|
||||
|
||||
|
||||
func query_keys_by_keycodes(keycodes: Array[Key]) -> Array[GameKey]:
|
||||
return query_keys(
|
||||
func(game_key: GameKey) -> bool: return game_key.props.keycode in keycodes
|
||||
)
|
||||
|
||||
|
||||
func key_query_respond(game_key: GameKey) -> void:
|
||||
if _queried_keys_limit == 0 or _queried_keys.size() < _queried_keys_limit:
|
||||
_queried_keys.append(game_key)
|
||||
|
||||
|
||||
func emit_key_pressed(game_key: GameKey) -> void:
|
||||
key_pressed.emit(game_key)
|
||||
|
||||
|
||||
func emit_key_input_event_emitted(game_key: GameKey, event: InputEventKey) -> void:
|
||||
key_input_event_emitted.emit(game_key, event)
|
||||
|
||||
|
||||
func emit_player_current_key_changed(game_key: GameKey) -> void:
|
||||
player_current_key_changed.emit(game_key)
|
||||
|
||||
|
||||
func emit_player_finished_move(game_key: GameKey) -> void:
|
||||
player_finished_move.emit(game_key)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region key generation
|
||||
func _generate_keys(layout_rows: Array[Array]) -> void:
|
||||
print("generating keys... '%s'" % LayoutConfig.current_layout.name())
|
||||
|
||||
_set_gap_to_size_ratio()
|
||||
for row: Array[KeyProps] in layout_rows:
|
||||
_set_row_key_scales_with_gaps(row)
|
||||
|
||||
var rows_amount := layout_rows.size()
|
||||
var row_width := _get_row_width(layout_rows)
|
||||
var row_offset_z: float = 0
|
||||
|
||||
for i in range(rows_amount):
|
||||
var row := layout_rows[i] as Array[KeyProps]
|
||||
row_offset_z = _generate_row(row, i, row_width, rows_amount, row_offset_z)
|
||||
func _generate_game_keys() -> void:
|
||||
print("generating keys...")
|
||||
_iterate_key_props_rows(_generate_game_key, LayoutConfig.layout_key_props_rows)
|
||||
|
||||
|
||||
func _generate_row(
|
||||
row: Array[KeyProps],
|
||||
row_index: int,
|
||||
row_width: float,
|
||||
rows_amount: int,
|
||||
row_offset_z: float
|
||||
) -> float:
|
||||
var key_pos := _get_row_key_starting_pos(
|
||||
row_index, rows_amount, row_width, row_offset_z
|
||||
func _generate_game_key(
|
||||
key_props: KeyProps,
|
||||
key_pos: Vector3,
|
||||
angle: float,
|
||||
_current_keys: Dictionary[Vector2i, Array]
|
||||
) -> void:
|
||||
var game_key := GameKey.instantiate_with_props(
|
||||
key_props, key_pos, angle, self, _key_scene
|
||||
)
|
||||
var key_pos_init := key_pos
|
||||
|
||||
for key_props in row:
|
||||
var key_default_pos := _get_row_key_pos_with_offset(key_pos, key_props)
|
||||
var game_key := GameKey.instantiate_with_props(key_props, key_default_pos, self)
|
||||
_keys_holder.add_child(game_key, true)
|
||||
|
||||
key_pos += _get_row_key_step(key_props)
|
||||
|
||||
return row_offset_z + key_pos_init.z - key_pos.z
|
||||
_keys_holder.add_child(game_key, true)
|
||||
|
||||
|
||||
func _set_row_key_scales_with_gaps(row: Array[KeyProps]) -> void:
|
||||
for key_props in row:
|
||||
func _iterate_key_props_rows(
|
||||
iter_func: Callable,
|
||||
key_props_rows: Array[Array],
|
||||
current_keys: Dictionary[Vector2i, Array] = {}
|
||||
) -> void:
|
||||
_gap_to_size_ratio = key_gap / key_size
|
||||
for key_props_row: Array[KeyProps] in key_props_rows:
|
||||
_set_row_key_scales_with_gaps(key_props_row)
|
||||
|
||||
var rect := KeyHelper.iterate_key_props_rows(
|
||||
iter_func, key_props_rows, key_size, key_gap, current_keys
|
||||
)
|
||||
|
||||
layout_changed.emit(rect)
|
||||
_rect_mesh.scale = Vector3(rect.size.x, 1, rect.size.y)
|
||||
_rect_mesh.position.x = rect.position.x + rect.size.x / 2 - rect.get_center().x
|
||||
_rect_mesh.position.z = rect.position.x + rect.size.y / 2 - rect.get_center().y
|
||||
|
||||
|
||||
func _set_row_key_scales_with_gaps(key_props_row: Array[KeyProps]) -> void:
|
||||
for key_props in key_props_row:
|
||||
key_props.width = _get_scale_with_gaps(key_props.width)
|
||||
key_props.height = _get_scale_with_gaps(key_props.height)
|
||||
key_props.x = _get_scale_with_gaps(key_props.x) + _gap_to_size_ratio
|
||||
@ -138,154 +217,99 @@ func _set_row_key_scales_with_gaps(row: Array[KeyProps]) -> void:
|
||||
key_props.height2 = _get_scale_with_gaps(key_props.height2)
|
||||
key_props.x2 = _get_scale_with_gaps(key_props.x2) + _gap_to_size_ratio
|
||||
key_props.y2 = _get_scale_with_gaps(key_props.y2) + _gap_to_size_ratio
|
||||
if key_props.has_pivot_x():
|
||||
key_props.pivot_x = (
|
||||
_get_scale_with_gaps(key_props.pivot_x) + _gap_to_size_ratio
|
||||
)
|
||||
if key_props.has_pivot_y():
|
||||
key_props.pivot_y = (
|
||||
_get_scale_with_gaps(key_props.pivot_y) + _gap_to_size_ratio
|
||||
)
|
||||
|
||||
|
||||
func _get_scale_with_gaps(key_scale: float) -> float:
|
||||
return key_scale + _gap_to_size_ratio * (key_scale - 1)
|
||||
|
||||
|
||||
func _get_row_width(layout_rows: Array[Array]) -> float:
|
||||
var max_width: float = 0
|
||||
for row: Array[KeyProps] in layout_rows:
|
||||
var width: float = 0
|
||||
for key_props in row:
|
||||
width += key_size * key_props.width
|
||||
var width_with_gaps := width + (row.size() - 1) * key_gap
|
||||
max_width = maxf(max_width, width_with_gaps)
|
||||
return max_width
|
||||
|
||||
|
||||
func _get_row_key_starting_pos(
|
||||
row_index: int, rows_amount: int, row_width: float, row_offset_z: float
|
||||
) -> Vector3:
|
||||
var offset_z: float = (
|
||||
((rows_amount - 1) * key_size + (rows_amount - 1) * key_gap) / 2 + row_offset_z
|
||||
)
|
||||
var key_pos_x: float = -row_width / 2 + key_size / 2
|
||||
var key_pos_z: float = row_index * key_size + row_index * key_gap - offset_z
|
||||
return Vector3(key_pos_x, 0, key_pos_z)
|
||||
|
||||
|
||||
func _get_row_key_pos_with_offset(key_pos: Vector3, key_props: KeyProps) -> Vector3:
|
||||
key_pos.x += (key_size * key_props.width - key_size) / 2 + key_size * key_props.x
|
||||
key_pos.z += key_size * key_props.y
|
||||
return key_pos
|
||||
|
||||
|
||||
func _get_row_key_step(key_props: KeyProps) -> Vector3:
|
||||
var step_x := key_size * key_props.width + key_gap + key_size * key_props.x
|
||||
var step_z := key_size * key_props.y
|
||||
return Vector3(step_x, 0, step_z)
|
||||
|
||||
|
||||
func _set_gap_to_size_ratio() -> void:
|
||||
_gap_to_size_ratio = key_gap / key_size
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region layout swapping
|
||||
func _swap_layout(kp_key: Key) -> void:
|
||||
func _change_layout(kp_key: Key) -> void:
|
||||
if kp_key == KEY_KP_0:
|
||||
prompt_page = wrapi(prompt_page + 1, 0, prompt_pages_total)
|
||||
prompt_page_turned.emit(prompt_page)
|
||||
return
|
||||
|
||||
var index := kp_key - KEY_KP_1
|
||||
if index < 0 or index >= LayoutConfig.layouts.size():
|
||||
var layout_idx := kp_key - KEY_KP_1 + prompt_page * 9
|
||||
if layout_idx < 0 or layout_idx >= LayoutConfig.layouts.size():
|
||||
return
|
||||
|
||||
var new_layout := (
|
||||
(LayoutConfig.layouts.values() as Array[AbstractLayout])[index].name()
|
||||
var new_layout_name := (
|
||||
(LayoutConfig.layouts.values() as Array[AbstractLayout])[layout_idx].get_name()
|
||||
)
|
||||
|
||||
if new_layout == LayoutConfig.current_layout.name():
|
||||
if new_layout_name == LayoutConfig.current_layout.get_name():
|
||||
return
|
||||
|
||||
LayoutConfig.swap_layout(new_layout)
|
||||
_regenerate_keys(LayoutConfig.layout_rows)
|
||||
LayoutConfig.change_layout(new_layout_name)
|
||||
_regenerate_keys(LayoutConfig.layout_key_props_rows)
|
||||
_play_sfx(_layout_swap_sfx)
|
||||
|
||||
|
||||
func _regenerate_keys(layout_rows: Array[Array]) -> void:
|
||||
print("REgenerating keys... '%s'" % LayoutConfig.current_layout.name())
|
||||
func _regenerate_keys(key_props_rows: Array[Array]) -> void:
|
||||
print("REgenerating keys...")
|
||||
|
||||
var current_keys: Dictionary[Vector2i, Array]
|
||||
|
||||
for node in _keys_holder.get_children():
|
||||
if node is not GameKey:
|
||||
continue
|
||||
var game_key := node as GameKey
|
||||
var key_props := game_key.props
|
||||
var dict_key := Vector2i(key_props.physical_keycode, key_props.location)
|
||||
var dict_key := Vector2i(key_props.keycode, key_props.location)
|
||||
if current_keys.has(dict_key):
|
||||
current_keys[dict_key].append(game_key)
|
||||
else:
|
||||
current_keys[dict_key] = [game_key] as Array[GameKey]
|
||||
|
||||
_set_gap_to_size_ratio()
|
||||
for row: Array[KeyProps] in layout_rows:
|
||||
_set_row_key_scales_with_gaps(row)
|
||||
|
||||
var rows_amount := layout_rows.size()
|
||||
var row_width := _get_row_width(layout_rows)
|
||||
var row_offset_z: float = 0
|
||||
|
||||
for i in range(rows_amount):
|
||||
var row := layout_rows[i] as Array[KeyProps]
|
||||
row_offset_z = _regenerate_row(
|
||||
row, i, row_width, rows_amount, row_offset_z, current_keys
|
||||
)
|
||||
_iterate_key_props_rows(_regenerate_key, key_props_rows, current_keys)
|
||||
|
||||
for game_keys: Array[GameKey] in current_keys.values():
|
||||
for game_key in game_keys:
|
||||
game_key.queue_free()
|
||||
|
||||
|
||||
func _regenerate_row(
|
||||
row: Array[KeyProps],
|
||||
row_index: int,
|
||||
row_width: float,
|
||||
rows_amount: int,
|
||||
row_offset_z: float,
|
||||
current_keys: Dictionary[Vector2i, Array],
|
||||
) -> float:
|
||||
var key_pos := _get_row_key_starting_pos(
|
||||
row_index, rows_amount, row_width, row_offset_z
|
||||
)
|
||||
var key_pos_init := key_pos
|
||||
func _regenerate_key(
|
||||
key_props: KeyProps,
|
||||
key_pos: Vector3,
|
||||
angle: float,
|
||||
current_keys: Dictionary[Vector2i, Array]
|
||||
) -> void:
|
||||
var dict_key := Vector2i(key_props.keycode, key_props.location)
|
||||
|
||||
for key_props in row:
|
||||
var key_default_pos := _get_row_key_pos_with_offset(key_pos, key_props)
|
||||
var dict_key := Vector2i(key_props.physical_keycode, key_props.location)
|
||||
if current_keys.has(dict_key):
|
||||
var game_keys := current_keys[dict_key] as Array[GameKey]
|
||||
var game_key_idx: int
|
||||
var min_dist: float = 0
|
||||
for i in range(game_keys.size()):
|
||||
var dist := game_keys[i].position.distance_to(key_pos)
|
||||
if min_dist == 0 or dist <= min_dist:
|
||||
min_dist = dist
|
||||
game_key_idx = i
|
||||
game_keys[game_key_idx].load_props(key_props, key_pos, angle)
|
||||
|
||||
if current_keys.has(dict_key):
|
||||
var game_keys := current_keys[dict_key] as Array[GameKey]
|
||||
var game_key_idx: int
|
||||
var min_dist: float = 0
|
||||
for i in range(game_keys.size()):
|
||||
var dist := game_keys[i].position.distance_to(key_default_pos)
|
||||
if min_dist == 0 or dist <= min_dist:
|
||||
min_dist = dist
|
||||
game_key_idx = i
|
||||
game_keys[game_key_idx].load_props(key_props, key_default_pos)
|
||||
game_keys.remove_at(game_key_idx)
|
||||
if game_keys.size() == 0:
|
||||
current_keys.erase(dict_key)
|
||||
else:
|
||||
var props_dict := LayoutConfig.get_key_config_dict(
|
||||
key_props.physical_keycode
|
||||
)
|
||||
if props_dict:
|
||||
key_props.chars_from_dict(props_dict)
|
||||
game_keys.remove_at(game_key_idx)
|
||||
if game_keys.size() == 0:
|
||||
current_keys.erase(dict_key)
|
||||
else:
|
||||
var chars_dict := LayoutConfig.get_key_config_dict(key_props.keycode)
|
||||
if chars_dict and chars_dict is Dictionary:
|
||||
key_props.chars_from_dict(chars_dict)
|
||||
|
||||
var game_key := GameKey.instantiate_with_props(
|
||||
key_props, key_default_pos, self
|
||||
)
|
||||
_keys_holder.add_child(game_key, true)
|
||||
|
||||
key_pos += _get_row_key_step(key_props)
|
||||
|
||||
return row_offset_z + key_pos_init.z - key_pos.z
|
||||
var game_key := GameKey.instantiate_with_props(
|
||||
key_props, key_pos, angle, self, _key_scene
|
||||
)
|
||||
_keys_holder.add_child(game_key, true)
|
||||
|
||||
|
||||
#endregion
|
||||
@ -294,16 +318,16 @@ func _regenerate_row(
|
||||
#region animation
|
||||
func _reset_animations() -> void:
|
||||
anim_time = 0
|
||||
_rot_sod = SecondOrderDynamics.new(_rot_sod_fzr, _default_rotation)
|
||||
_rot_sod = SecondOrderDynamics.new(_rot_sod_fzr, _default_rot)
|
||||
|
||||
|
||||
func _animate(delta: float) -> void:
|
||||
anim_time += delta * _time_scale
|
||||
rotation = _rot_sod.process(delta, _animate_rotation())
|
||||
anim_time += delta * _anim_time_scale
|
||||
_keys_holder.rotation = _rot_sod.process(delta, _animate_rotation())
|
||||
|
||||
|
||||
func _animate_rotation() -> Vector3:
|
||||
var new_rotation := _default_rotation
|
||||
var new_rotation := _default_rot
|
||||
for pos: Vector3 in pressed_positions.keys():
|
||||
var pos_pressed := pressed_positions[pos]
|
||||
if not pos_pressed:
|
||||
@ -320,4 +344,37 @@ func _animate_rotation() -> Vector3:
|
||||
#region sounds
|
||||
func _play_sfx(stream: AudioStream) -> void:
|
||||
_polyphonic.play_stream(stream)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region prompt
|
||||
func _set_prompt_pages() -> void:
|
||||
prompt_page = 0
|
||||
prompt_pages_total = ceili(LayoutConfig.layouts.size() / 9.0)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region editor
|
||||
func _generate_editor_keys() -> void:
|
||||
_delete_editor_keys()
|
||||
_iterate_key_props_rows(_generate_game_key, LayoutANSI.new().get_key_props_rows())
|
||||
|
||||
|
||||
func _delete_editor_keys() -> void:
|
||||
pressed_positions = {}
|
||||
var nodes := _keys_holder.get_children()
|
||||
if not nodes:
|
||||
return
|
||||
print("deleting keys...")
|
||||
for node in nodes:
|
||||
node.queue_free()
|
||||
|
||||
|
||||
func _query_editor_keys() -> void:
|
||||
print(query_key_by_keycode(KEY_SHIFT))
|
||||
|
||||
#endregion
|
||||
|
||||
@ -3,40 +3,32 @@ extends Node
|
||||
signal layout_swapped(new_layout: AbstractLayout)
|
||||
|
||||
const CONFIG_PATH := "user://layout.cfg"
|
||||
const CUSTOM_LAYOUTS_PATH := "user://custom_layouts"
|
||||
const CUSTOM_LAYOUTS_README := "res://resources/kle/README.txt"
|
||||
const SECTION_INFO := "info"
|
||||
const SECTION_KEYS := "keys"
|
||||
const PARAM_NAME := "name"
|
||||
|
||||
var current_layout: AbstractLayout
|
||||
var layout_rows: Array[Array]
|
||||
var is_configuring: bool = false
|
||||
var layout_key_props_rows: Array[Array]
|
||||
|
||||
var layouts: Dictionary[String, AbstractLayout] = {
|
||||
LayoutANSI.name_static(): LayoutANSI.new(),
|
||||
LayoutANSIVariant.name_static(): LayoutANSIVariant.new(),
|
||||
LayoutISO.name_static(): LayoutISO.new(),
|
||||
LayoutABNT.name_static(): LayoutABNT.new(),
|
||||
LayoutJIS.name_static(): LayoutJIS.new(),
|
||||
# LayoutJD40.name_static(): LayoutJD40.new(),
|
||||
# LayoutPlanck.name_static(): LayoutPlanck.new(),
|
||||
"ansi-104.json": LayoutKLE.new("res://scripts/layouts/kle/ansi-104.json"),
|
||||
"ansi-104-big-enter.json":
|
||||
LayoutKLE.new("res://scripts/layouts/kle/ansi-104-big-enter.json"),
|
||||
"iso-105.json": LayoutKLE.new("res://scripts/layouts/kle/iso-105.json"),
|
||||
"keycool-84.json": LayoutKLE.new("res://scripts/layouts/kle/keycool-84.json"),
|
||||
# LayoutTest.name_static(): LayoutTest.new(),
|
||||
LayoutANSI.name: LayoutANSI.new(),
|
||||
LayoutANSIVariant.name: LayoutANSIVariant.new(),
|
||||
LayoutISO.name: LayoutISO.new(),
|
||||
LayoutABNT.name: LayoutABNT.new(),
|
||||
LayoutJIS.name: LayoutJIS.new(),
|
||||
# LayoutTest.name: LayoutTest.new(),
|
||||
}
|
||||
|
||||
var _config := ConfigFile.new()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_load_custom_layouts(CUSTOM_LAYOUTS_PATH)
|
||||
var err := _load_config()
|
||||
if err != OK:
|
||||
print("layout config not loaded.....")
|
||||
if err:
|
||||
_set_defaults()
|
||||
# _set_defaults()
|
||||
is_configuring = true
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
@ -45,101 +37,141 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
print("layout config saved!")
|
||||
|
||||
|
||||
func get_key_config_dict(keycode: Key) -> Dictionary[KeyProps.Char, String]:
|
||||
var param_key := str(keycode)
|
||||
if not _config.has_section_key(SECTION_KEYS, param_key):
|
||||
func get_key_config_dict(keycode: Key) -> Dictionary:
|
||||
var config_key := OS.get_keycode_string(keycode)
|
||||
if not _config.has_section_key(SECTION_KEYS, config_key):
|
||||
return {}
|
||||
|
||||
return (
|
||||
_config.get_value(SECTION_KEYS, param_key, {})
|
||||
as Dictionary[KeyProps.Char, String]
|
||||
)
|
||||
return _config.get_value(SECTION_KEYS, config_key, {}) as Dictionary
|
||||
|
||||
|
||||
func swap_layout(layout_name: String) -> void:
|
||||
func change_layout(layout_name: String) -> void:
|
||||
if layouts.has(layout_name):
|
||||
_set_layout(layouts[layout_name])
|
||||
layout_swapped.emit(current_layout)
|
||||
else:
|
||||
printerr("no such layout! ", layout_name)
|
||||
push_error("layout '%s' not found" % layout_name)
|
||||
|
||||
|
||||
func _set_defaults() -> void:
|
||||
print("setting defaults")
|
||||
print("\nsetting defaults")
|
||||
_set_layout(layouts.values()[0] as AbstractLayout)
|
||||
# _set_layout(layouts[LayoutANSI.name_static()])
|
||||
|
||||
|
||||
func _set_layout(layout: AbstractLayout) -> void:
|
||||
print("SETTING LAYOUT: '%s'" % layout.name())
|
||||
print("SETTING LAYOUT: '%s'" % layout.get_name())
|
||||
current_layout = layout
|
||||
|
||||
layout_rows = []
|
||||
for dict_row: Array[Dictionary] in current_layout.rows():
|
||||
var row: Array[KeyProps] = []
|
||||
for props_dict: Dictionary in dict_row:
|
||||
row.append(KeyProps.new().props_from_dict(props_dict))
|
||||
layout_rows.append(row)
|
||||
layout_key_props_rows = current_layout.get_key_props_rows()
|
||||
|
||||
|
||||
func _load_config() -> Error:
|
||||
print("loading layout config....")
|
||||
print("\nloading layout config....")
|
||||
var err := _config.load(CONFIG_PATH)
|
||||
|
||||
if err == ERR_FILE_NOT_FOUND:
|
||||
print("layout config file not found")
|
||||
return FAILED
|
||||
|
||||
if err != OK:
|
||||
printerr("failed to load layout config file: '%s'" % err)
|
||||
return FAILED
|
||||
if err:
|
||||
push_error("failed to load layout config file: '%s'" % error_string(err))
|
||||
print()
|
||||
return err
|
||||
|
||||
var layout_name := ""
|
||||
if _config.has_section_key(SECTION_INFO, PARAM_NAME):
|
||||
var layout_name := _config.get_value(SECTION_INFO, PARAM_NAME, "") as String
|
||||
if layouts.has(layout_name):
|
||||
_set_layout(layouts[layout_name])
|
||||
else:
|
||||
printerr("layout '%s' not found" % layout_name)
|
||||
_set_defaults()
|
||||
var config_layout_name: Variant = _config.get_value(SECTION_INFO, PARAM_NAME)
|
||||
if config_layout_name is String:
|
||||
layout_name = config_layout_name as String
|
||||
else:
|
||||
printerr("layout config is missing '%s/%s'" % [SECTION_INFO, PARAM_NAME])
|
||||
push_error("layout config is missing '%s/%s'" % [SECTION_INFO, PARAM_NAME])
|
||||
|
||||
if layout_name != "" and layouts.has(layout_name):
|
||||
_set_layout(layouts[layout_name])
|
||||
else:
|
||||
push_error("layout '%s' not found" % layout_name)
|
||||
_set_defaults()
|
||||
|
||||
for row: Array[KeyProps] in layout_rows:
|
||||
for key_props in row:
|
||||
var param_key := str(key_props.physical_keycode)
|
||||
for key_props_row: Array[KeyProps] in layout_key_props_rows:
|
||||
for key_props in key_props_row:
|
||||
var config_key := OS.get_keycode_string(key_props.keycode)
|
||||
if (
|
||||
not key_props.is_char()
|
||||
or not _config.has_section_key(SECTION_KEYS, param_key)
|
||||
not key_props.is_unicode()
|
||||
or not _config.has_section_key(SECTION_KEYS, config_key)
|
||||
):
|
||||
continue
|
||||
|
||||
var dictionary := (
|
||||
_config.get_value(SECTION_KEYS, param_key, {}) as Dictionary
|
||||
)
|
||||
key_props.chars_from_dict(dictionary)
|
||||
var chars_dict: Variant = _config.get_value(SECTION_KEYS, config_key)
|
||||
if chars_dict is not Dictionary:
|
||||
continue
|
||||
key_props.chars_from_dict(chars_dict as Dictionary)
|
||||
|
||||
print("layout config loaded!")
|
||||
print("layout config loaded!\n")
|
||||
|
||||
return OK
|
||||
|
||||
|
||||
func _save_config() -> Error:
|
||||
_config.set_value(SECTION_INFO, PARAM_NAME, current_layout.name())
|
||||
_config.set_value(SECTION_INFO, PARAM_NAME, current_layout.get_name())
|
||||
|
||||
for row: Array[KeyProps] in layout_rows:
|
||||
for key_props in row:
|
||||
if not key_props.is_char():
|
||||
for key_props_row: Array[KeyProps] in layout_key_props_rows:
|
||||
for key_props in key_props_row:
|
||||
if not key_props.is_unicode():
|
||||
continue
|
||||
|
||||
_config.set_value(
|
||||
SECTION_KEYS, str(key_props.physical_keycode), key_props.chars_to_dict()
|
||||
SECTION_KEYS,
|
||||
OS.get_keycode_string(key_props.keycode),
|
||||
key_props.chars_to_dict()
|
||||
)
|
||||
|
||||
var err := _config.save(CONFIG_PATH)
|
||||
|
||||
if err != OK:
|
||||
printerr("failed to save config file: '%s'" % err)
|
||||
if err:
|
||||
push_error("failed to save config file: '%s'" % error_string(err))
|
||||
return err
|
||||
|
||||
return OK
|
||||
|
||||
|
||||
func _load_custom_layouts(path: String) -> void:
|
||||
if not DirAccess.dir_exists_absolute(path):
|
||||
var dir_err := DirAccess.make_dir_absolute(path)
|
||||
if dir_err:
|
||||
push_error(
|
||||
"error creating directory '%s': %s" % [path, error_string(dir_err)]
|
||||
)
|
||||
print()
|
||||
return
|
||||
var file_err := DirAccess.copy_absolute(
|
||||
CUSTOM_LAYOUTS_README, path.path_join(CUSTOM_LAYOUTS_README.get_file())
|
||||
)
|
||||
if file_err:
|
||||
push_error(
|
||||
(
|
||||
"error creating file '%s': %s"
|
||||
% [CUSTOM_LAYOUTS_README, error_string(file_err)]
|
||||
)
|
||||
)
|
||||
|
||||
var dir := DirAccess.open(path)
|
||||
if not dir:
|
||||
var err := DirAccess.get_open_error()
|
||||
push_error("error opening directory '%s': %s" % [path, error_string(err)])
|
||||
print()
|
||||
return
|
||||
|
||||
var file_names := dir.get_files()
|
||||
|
||||
if not file_names:
|
||||
return
|
||||
|
||||
print("loading custom layouts..\n")
|
||||
for file_name in file_names:
|
||||
if file_name.get_extension().to_lower() != "json":
|
||||
continue
|
||||
var layout := LayoutCustom.new(path.path_join(file_name))
|
||||
if layout.failed:
|
||||
continue
|
||||
if layouts.has(layout.get_name()):
|
||||
push_warning(
|
||||
(
|
||||
"layout '%s' already exists and will be replaced by the new one"
|
||||
% [layout.get_name()]
|
||||
)
|
||||
)
|
||||
layouts[layout.get_name()] = layout
|
||||
|
||||
150
scripts/key_helper.gd
Normal file
150
scripts/key_helper.gd
Normal file
@ -0,0 +1,150 @@
|
||||
class_name KeyHelper
|
||||
|
||||
const ADJACENCY_MAP: Dictionary[Key, Array] = {
|
||||
KEY_1: [KEY_2, KEY_Q],
|
||||
KEY_2: [KEY_3, KEY_W, KEY_Q, KEY_1],
|
||||
KEY_3: [KEY_4, KEY_E, KEY_W, KEY_2],
|
||||
KEY_4: [KEY_5, KEY_R, KEY_E, KEY_3],
|
||||
KEY_5: [KEY_6, KEY_T, KEY_R, KEY_4],
|
||||
KEY_6: [KEY_7, KEY_Y, KEY_T, KEY_5],
|
||||
KEY_7: [KEY_8, KEY_U, KEY_Y, KEY_6],
|
||||
KEY_8: [KEY_9, KEY_I, KEY_U, KEY_7],
|
||||
KEY_9: [KEY_0, KEY_O, KEY_I, KEY_8],
|
||||
KEY_0: [KEY_MINUS, KEY_P, KEY_O, KEY_9],
|
||||
KEY_MINUS: [KEY_EQUAL, KEY_BRACKETLEFT, KEY_P, KEY_0],
|
||||
KEY_EQUAL: [KEY_BRACKETLEFT, KEY_BRACKETRIGHT, KEY_MINUS],
|
||||
KEY_Q: [KEY_1, KEY_2, KEY_W, KEY_A],
|
||||
KEY_W: [KEY_2, KEY_3, KEY_E, KEY_S, KEY_A, KEY_Q],
|
||||
KEY_E: [KEY_3, KEY_4, KEY_R, KEY_D, KEY_S, KEY_W],
|
||||
KEY_R: [KEY_4, KEY_5, KEY_T, KEY_F, KEY_D, KEY_E],
|
||||
KEY_T: [KEY_5, KEY_6, KEY_Y, KEY_G, KEY_F, KEY_R],
|
||||
KEY_Y: [KEY_6, KEY_7, KEY_U, KEY_H, KEY_G, KEY_T],
|
||||
KEY_U: [KEY_7, KEY_8, KEY_I, KEY_J, KEY_H, KEY_Y],
|
||||
KEY_I: [KEY_8, KEY_9, KEY_O, KEY_K, KEY_J, KEY_U],
|
||||
KEY_O: [KEY_9, KEY_0, KEY_P, KEY_L, KEY_K, KEY_I],
|
||||
KEY_P: [KEY_0, KEY_MINUS, KEY_BRACKETLEFT, KEY_SEMICOLON, KEY_L, KEY_O],
|
||||
KEY_BRACKETLEFT:
|
||||
[KEY_MINUS, KEY_EQUAL, KEY_BRACKETRIGHT, KEY_APOSTROPHE, KEY_SEMICOLON, KEY_P],
|
||||
KEY_BRACKETRIGHT: [KEY_EQUAL, KEY_APOSTROPHE, KEY_BRACKETLEFT],
|
||||
KEY_A: [KEY_Q, KEY_W, KEY_S, KEY_Z],
|
||||
KEY_S: [KEY_W, KEY_E, KEY_D, KEY_X, KEY_Z, KEY_A],
|
||||
KEY_D: [KEY_E, KEY_R, KEY_F, KEY_C, KEY_X, KEY_S],
|
||||
KEY_F: [KEY_R, KEY_T, KEY_G, KEY_V, KEY_C, KEY_D],
|
||||
KEY_G: [KEY_T, KEY_Y, KEY_H, KEY_B, KEY_V, KEY_F],
|
||||
KEY_H: [KEY_Y, KEY_U, KEY_J, KEY_N, KEY_B, KEY_G],
|
||||
KEY_J: [KEY_U, KEY_I, KEY_K, KEY_M, KEY_N, KEY_H],
|
||||
KEY_K: [KEY_I, KEY_O, KEY_L, KEY_COMMA, KEY_M, KEY_J],
|
||||
KEY_L: [KEY_O, KEY_P, KEY_SEMICOLON, KEY_PERIOD, KEY_COMMA, KEY_K],
|
||||
KEY_SEMICOLON:
|
||||
[KEY_P, KEY_BRACKETLEFT, KEY_APOSTROPHE, KEY_SLASH, KEY_PERIOD, KEY_L],
|
||||
KEY_APOSTROPHE: [KEY_BRACKETLEFT, KEY_BRACKETRIGHT, KEY_SLASH, KEY_SEMICOLON],
|
||||
KEY_Z: [KEY_A, KEY_S, KEY_X],
|
||||
KEY_X: [KEY_S, KEY_D, KEY_C, KEY_Z],
|
||||
KEY_C: [KEY_D, KEY_F, KEY_V, KEY_X],
|
||||
KEY_V: [KEY_F, KEY_G, KEY_B, KEY_C],
|
||||
KEY_B: [KEY_G, KEY_H, KEY_N, KEY_V],
|
||||
KEY_N: [KEY_H, KEY_J, KEY_M, KEY_B],
|
||||
KEY_M: [KEY_J, KEY_K, KEY_COMMA, KEY_N],
|
||||
KEY_COMMA: [KEY_K, KEY_L, KEY_PERIOD, KEY_M],
|
||||
KEY_PERIOD: [KEY_L, KEY_SEMICOLON, KEY_SLASH, KEY_COMMA],
|
||||
KEY_SLASH: [KEY_SEMICOLON, KEY_APOSTROPHE, KEY_PERIOD],
|
||||
}
|
||||
|
||||
|
||||
static func game_key_sort(a: GameKey, b: GameKey) -> bool:
|
||||
var a_pos := a.get_default_transform().origin
|
||||
var b_pos := b.get_default_transform().origin
|
||||
if a_pos.y == b_pos.y:
|
||||
return a_pos.x < b_pos.x
|
||||
return a_pos.y < b_pos.y
|
||||
|
||||
|
||||
static func get_rotated_key_pos(
|
||||
key_pos: Vector3, pivot: Vector2, angle: float
|
||||
) -> Vector3:
|
||||
var pivot_pos := Vector3(pivot.x, 0, pivot.y)
|
||||
return (key_pos - pivot_pos).rotated(Vector3.UP, angle) + pivot_pos
|
||||
|
||||
|
||||
static func iterate_key_props_rows(
|
||||
iter_func: Callable,
|
||||
key_props_rows: Array[Array],
|
||||
key_size: float = 1,
|
||||
key_gap: float = 0,
|
||||
current_keys: Dictionary[Vector2i, Array] = {}
|
||||
) -> Rect2:
|
||||
var key_pos: Vector2 = Vector2.ZERO
|
||||
var pivot := Vector2.ZERO
|
||||
var angle: float = 0
|
||||
var rect: Rect2 = Rect2(0, 0, 0, 0)
|
||||
|
||||
for key_props_row: Array[KeyProps] in key_props_rows:
|
||||
var result := _iterate_key_props_row(
|
||||
iter_func,
|
||||
key_props_row,
|
||||
key_pos,
|
||||
pivot,
|
||||
angle,
|
||||
rect,
|
||||
key_size,
|
||||
key_gap,
|
||||
current_keys
|
||||
)
|
||||
key_pos = result[0]
|
||||
key_pos.y += 1 + key_gap
|
||||
pivot = result[1]
|
||||
angle = result[2]
|
||||
rect = result[3]
|
||||
|
||||
return rect
|
||||
|
||||
|
||||
static func _iterate_key_props_row(
|
||||
iter_func: Callable,
|
||||
key_props_row: Array[KeyProps],
|
||||
key_pos: Vector2,
|
||||
pivot: Vector2,
|
||||
angle: float,
|
||||
rect: Rect2,
|
||||
key_size: float,
|
||||
key_gap: float,
|
||||
current_keys: Dictionary[Vector2i, Array]
|
||||
) -> Array[Variant]:
|
||||
key_pos.x = pivot.x
|
||||
|
||||
for key_props in key_props_row:
|
||||
if key_props.has_angle():
|
||||
angle = -deg_to_rad(key_props.angle)
|
||||
if key_props.has_pivot_x():
|
||||
pivot.x = key_props.pivot_x
|
||||
if key_props.has_pivot_y():
|
||||
pivot.y = key_props.pivot_y
|
||||
if key_props.has_pivot_x() or key_props.has_pivot_y():
|
||||
key_pos = pivot
|
||||
|
||||
var game_key_pos := Vector3(
|
||||
key_pos.x + key_size / 2, 0, key_pos.y + key_size / 2
|
||||
)
|
||||
game_key_pos.x += (
|
||||
(key_size * key_props.width - key_size) / 2 + key_size * key_props.x
|
||||
)
|
||||
game_key_pos.z += key_props.y * key_size
|
||||
|
||||
game_key_pos = KeyHelper.get_rotated_key_pos(game_key_pos, pivot, angle)
|
||||
|
||||
iter_func.call(key_props, game_key_pos, angle, current_keys)
|
||||
|
||||
if key_pos.x < rect.position.x:
|
||||
rect.position.x = key_pos.x
|
||||
if key_pos.y < rect.position.y:
|
||||
rect.position.y = key_pos.y
|
||||
|
||||
key_pos.x += key_props.width * key_size + key_props.x * key_size + key_gap
|
||||
key_pos.y += key_props.y * key_size
|
||||
|
||||
if key_pos.x - key_gap > rect.end.x:
|
||||
rect.end.x = key_pos.x - key_gap
|
||||
if key_pos.y + key_size * key_props.height > rect.end.y:
|
||||
rect.end.y = key_pos.y + key_size * key_props.height
|
||||
|
||||
return [key_pos, pivot, angle, rect]
|
||||
1
scripts/key_helper.gd.uid
Normal file
1
scripts/key_helper.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://boau8xfetthx8
|
||||
@ -1,24 +1,28 @@
|
||||
class_name KeyProps
|
||||
@tool
|
||||
class_name KeyProps extends Resource
|
||||
|
||||
enum Char { MAIN, SHIFT, ALT, ALT_SHIFT }
|
||||
const CHAR_MAIN = "char_main"
|
||||
const CHAR_SHIFT = "char_shift"
|
||||
const CHAR_ALT = "char_alt"
|
||||
const CHAR_ALT_SHIFT = "char_alt_shift"
|
||||
|
||||
const KEY := "key"
|
||||
const LOC := "location"
|
||||
const KEY = "key"
|
||||
const LOC = "loc"
|
||||
const W = "w"
|
||||
const H = "h"
|
||||
const X = "x"
|
||||
const Y = "y"
|
||||
const W2 = "w2"
|
||||
const H2 = "h2"
|
||||
const X2 = "x2"
|
||||
const Y2 = "y2"
|
||||
const R = "r"
|
||||
const PX = "rx"
|
||||
const PY = "ry"
|
||||
const NUB = "nub"
|
||||
|
||||
const W := "w"
|
||||
const H := "h"
|
||||
const X := "x"
|
||||
const Y := "y"
|
||||
|
||||
const SECONDARY_RECT := "secondary_rect"
|
||||
const W2 := "w2"
|
||||
const H2 := "h2"
|
||||
const X2 := "x2"
|
||||
const Y2 := "y2"
|
||||
|
||||
var physical_keycode: Key
|
||||
var keycode: Key = KEY_NONE
|
||||
var location: KeyLocation = KEY_LOCATION_UNSPECIFIED
|
||||
var secondary_rect: bool = false
|
||||
|
||||
var width: float = 1
|
||||
var height: float = 1
|
||||
@ -26,25 +30,49 @@ var x: float = 0
|
||||
var y: float = 0
|
||||
|
||||
var width2: float = 1
|
||||
var width_init2: float = 1
|
||||
var height2: float = 1
|
||||
var height_init2: float = 1
|
||||
var x2: float = 0
|
||||
var y2: float = 0
|
||||
|
||||
var angle: float = -INF
|
||||
var pivot_x: float = -INF
|
||||
var pivot_y: float = -INF
|
||||
|
||||
var homing_nub: bool = false
|
||||
|
||||
var main_char: String
|
||||
var shift_char: String
|
||||
var alt_char: String
|
||||
var alt_shift_char: String
|
||||
|
||||
|
||||
func is_char() -> bool:
|
||||
return OS.is_keycode_unicode(physical_keycode)
|
||||
func is_unicode() -> bool:
|
||||
return keycode != KEY_SPACE and OS.is_keycode_unicode(keycode)
|
||||
|
||||
|
||||
func is_player_pos_key() -> bool:
|
||||
return KeyHelper.ADJACENCY_MAP.has(keycode)
|
||||
|
||||
|
||||
func has_secondary_rect() -> bool:
|
||||
return width2 != 1 or height2 != 1 or x2 != 0 or y2 != 0
|
||||
|
||||
|
||||
func has_angle() -> bool:
|
||||
return angle != -INF
|
||||
|
||||
|
||||
func has_pivot_x() -> bool:
|
||||
return pivot_x != -INF
|
||||
|
||||
|
||||
func has_pivot_y() -> bool:
|
||||
return pivot_y != -INF
|
||||
|
||||
|
||||
func props_from_dict(dict: Dictionary) -> KeyProps:
|
||||
if dict.has(KEY):
|
||||
physical_keycode = dict[KEY]
|
||||
keycode = dict[KEY]
|
||||
if dict.has(LOC):
|
||||
location = dict[LOC]
|
||||
|
||||
@ -57,31 +85,37 @@ func props_from_dict(dict: Dictionary) -> KeyProps:
|
||||
if dict.has(Y):
|
||||
y = dict[Y]
|
||||
|
||||
if dict.has(SECONDARY_RECT):
|
||||
secondary_rect = dict[SECONDARY_RECT]
|
||||
if dict.has(W2):
|
||||
width2 = dict[W2]
|
||||
width_init2 = dict[W2]
|
||||
if dict.has(H2):
|
||||
height2 = dict[H2]
|
||||
height_init2 = dict[H2]
|
||||
if dict.has(X2):
|
||||
x2 = dict[X2]
|
||||
if dict.has(Y2):
|
||||
y2 = dict[Y2]
|
||||
|
||||
if dict.has(R):
|
||||
angle = dict[R]
|
||||
if dict.has(PX):
|
||||
pivot_x = dict[PX]
|
||||
if dict.has(PY):
|
||||
pivot_y = dict[PY]
|
||||
|
||||
if dict.has(NUB):
|
||||
homing_nub = dict[NUB]
|
||||
|
||||
return self
|
||||
|
||||
|
||||
func chars_from_dict(dict: Dictionary[Char, String], override: bool = true) -> KeyProps:
|
||||
if dict.has(Char.MAIN) and (override or not main_char):
|
||||
main_char = dict[Char.MAIN]
|
||||
if dict.has(Char.SHIFT) and (override or not shift_char):
|
||||
shift_char = dict[Char.SHIFT]
|
||||
if dict.has(Char.ALT) and (override or not alt_char):
|
||||
alt_char = dict[Char.ALT]
|
||||
if dict.has(Char.ALT_SHIFT) and (override or not alt_shift_char):
|
||||
alt_shift_char = dict[Char.ALT_SHIFT]
|
||||
func chars_from_dict(dict: Dictionary, override: bool = true) -> KeyProps:
|
||||
if dict.has(CHAR_MAIN) and (override or not main_char):
|
||||
main_char = dict[CHAR_MAIN]
|
||||
if dict.has(CHAR_SHIFT) and (override or not shift_char):
|
||||
shift_char = dict[CHAR_SHIFT]
|
||||
if dict.has(CHAR_ALT) and (override or not alt_char):
|
||||
alt_char = dict[CHAR_ALT]
|
||||
if dict.has(CHAR_ALT_SHIFT) and (override or not alt_shift_char):
|
||||
alt_shift_char = dict[CHAR_ALT_SHIFT]
|
||||
|
||||
if main_char == shift_char:
|
||||
shift_char = ""
|
||||
@ -93,15 +127,15 @@ func chars_from_dict(dict: Dictionary[Char, String], override: bool = true) -> K
|
||||
return self
|
||||
|
||||
|
||||
func chars_to_dict() -> Dictionary[Char, String]:
|
||||
var dict: Dictionary[Char, String] = {}
|
||||
func chars_to_dict() -> Dictionary:
|
||||
var dict: Dictionary = {}
|
||||
if main_char:
|
||||
dict[Char.MAIN] = main_char
|
||||
dict[CHAR_MAIN] = main_char
|
||||
if shift_char:
|
||||
dict[Char.SHIFT] = shift_char
|
||||
dict[CHAR_SHIFT] = shift_char
|
||||
if alt_char:
|
||||
dict[Char.ALT] = alt_char
|
||||
dict[CHAR_ALT] = alt_char
|
||||
if alt_shift_char:
|
||||
dict[Char.ALT_SHIFT] = alt_shift_char
|
||||
dict[CHAR_ALT_SHIFT] = alt_shift_char
|
||||
|
||||
return dict
|
||||
|
||||
@ -1,13 +1,19 @@
|
||||
class_name AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
func get_name() -> String:
|
||||
return ""
|
||||
|
||||
|
||||
func name() -> String:
|
||||
return name_static()
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
func get_key_dict_rows() -> Array[Array]:
|
||||
return []
|
||||
|
||||
|
||||
func get_key_props_rows() -> Array[Array]:
|
||||
var rows: Array[Array] = []
|
||||
for dict_row: Array[Dictionary] in get_key_dict_rows():
|
||||
var row: Array[KeyProps] = []
|
||||
for props_dict: Dictionary in dict_row:
|
||||
row.append(KeyProps.new().props_from_dict(props_dict))
|
||||
rows.append(row)
|
||||
return rows
|
||||
|
||||
13
scripts/layouts/abstract_parser.gd
Normal file
13
scripts/layouts/abstract_parser.gd
Normal file
@ -0,0 +1,13 @@
|
||||
class_name AbstractParser
|
||||
|
||||
|
||||
func get_name() -> String:
|
||||
return ""
|
||||
|
||||
|
||||
func get_rows() -> Array[Array]:
|
||||
return []
|
||||
|
||||
|
||||
func has_errors() -> bool:
|
||||
return false
|
||||
1
scripts/layouts/abstract_parser.gd.uid
Normal file
1
scripts/layouts/abstract_parser.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://ce0ominvxrn7v
|
||||
@ -1,4 +1,26 @@
|
||||
class_name CommonLayout
|
||||
class_name CommonKeys
|
||||
|
||||
|
||||
static func big_enter_iso() -> Dictionary:
|
||||
return {
|
||||
KeyProps.KEY: KEY_ENTER,
|
||||
KeyProps.W: 1.25,
|
||||
KeyProps.H: 2,
|
||||
KeyProps.X: 0.25,
|
||||
KeyProps.W2: 1.5,
|
||||
KeyProps.X2: -0.25,
|
||||
}
|
||||
|
||||
|
||||
static func big_enter_ansi() -> Dictionary:
|
||||
return {
|
||||
KeyProps.KEY: KEY_ENTER,
|
||||
KeyProps.W: 1.5,
|
||||
KeyProps.H: 2,
|
||||
KeyProps.W2: 2.25,
|
||||
KeyProps.Y2: 1,
|
||||
KeyProps.X2: -0.75,
|
||||
}
|
||||
|
||||
|
||||
static func numbers() -> Array:
|
||||
@ -51,10 +73,10 @@ static func letters_2() -> Array:
|
||||
{KeyProps.KEY: KEY_A},
|
||||
{KeyProps.KEY: KEY_S},
|
||||
{KeyProps.KEY: KEY_D},
|
||||
{KeyProps.KEY: KEY_F},
|
||||
{KeyProps.KEY: KEY_F, KeyProps.NUB: true},
|
||||
{KeyProps.KEY: KEY_G},
|
||||
{KeyProps.KEY: KEY_H},
|
||||
{KeyProps.KEY: KEY_J},
|
||||
{KeyProps.KEY: KEY_J, KeyProps.NUB: true},
|
||||
{KeyProps.KEY: KEY_K},
|
||||
{KeyProps.KEY: KEY_L},
|
||||
]
|
||||
@ -1,31 +1,23 @@
|
||||
class_name LayoutABNT extends AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
return "ABNT"
|
||||
static var name := "ABNT"
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
func get_name() -> String:
|
||||
return name
|
||||
|
||||
|
||||
func get_key_dict_rows() -> Array[Array]:
|
||||
return [
|
||||
CommonLayout.number_row() + [{KeyProps.KEY: KEY_BACKSPACE, KeyProps.W: 2}],
|
||||
CommonKeys.number_row() + [{KeyProps.KEY: KEY_BACKSPACE, KeyProps.W: 2}],
|
||||
(
|
||||
[{KeyProps.KEY: KEY_TAB, KeyProps.W: 1.5}]
|
||||
+ CommonLayout.letters_row_1()
|
||||
+ [
|
||||
{
|
||||
KeyProps.KEY: KEY_ENTER,
|
||||
KeyProps.W: 1.25,
|
||||
KeyProps.H: 2,
|
||||
KeyProps.X: 0.25,
|
||||
KeyProps.SECONDARY_RECT: true,
|
||||
KeyProps.W2: 1.5,
|
||||
KeyProps.X2: -0.25,
|
||||
}
|
||||
]
|
||||
+ CommonKeys.letters_row_1()
|
||||
+ [CommonKeys.big_enter_iso()]
|
||||
),
|
||||
(
|
||||
[{KeyProps.KEY: KEY_CAPSLOCK, KeyProps.W: 1.75}]
|
||||
+ CommonLayout.letters_row_2()
|
||||
+ CommonKeys.letters_row_2()
|
||||
+ [{KeyProps.KEY: KEY_BACKSLASH}]
|
||||
),
|
||||
(
|
||||
@ -37,9 +29,9 @@ func rows() -> Array[Array]:
|
||||
},
|
||||
{KeyProps.KEY: KEY_SECTION},
|
||||
]
|
||||
+ CommonLayout.letters_row_3()
|
||||
+ CommonKeys.letters_row_3()
|
||||
+ [
|
||||
{KeyProps.KEY: KEY_UNKNOWN},
|
||||
{KeyProps.KEY: KEY_NONE},
|
||||
{
|
||||
KeyProps.KEY: KEY_SHIFT,
|
||||
KeyProps.W: 1.75,
|
||||
@ -47,5 +39,5 @@ func rows() -> Array[Array]:
|
||||
}
|
||||
]
|
||||
),
|
||||
CommonLayout.bottom_row()
|
||||
CommonKeys.bottom_row()
|
||||
]
|
||||
|
||||
@ -1,21 +1,23 @@
|
||||
class_name LayoutANSI extends AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
return "ANSI"
|
||||
static var name := "ANSI"
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
func get_name() -> String:
|
||||
return name
|
||||
|
||||
|
||||
func get_key_dict_rows() -> Array[Array]:
|
||||
return [
|
||||
CommonLayout.number_row() + [{KeyProps.KEY: KEY_BACKSPACE, KeyProps.W: 2}],
|
||||
CommonKeys.number_row() + [{KeyProps.KEY: KEY_BACKSPACE, KeyProps.W: 2}],
|
||||
(
|
||||
[{KeyProps.KEY: KEY_TAB, KeyProps.W: 1.5}]
|
||||
+ CommonLayout.letters_row_1()
|
||||
+ CommonKeys.letters_row_1()
|
||||
+ [{KeyProps.KEY: KEY_BACKSLASH, KeyProps.W: 1.5}]
|
||||
),
|
||||
(
|
||||
[{KeyProps.KEY: KEY_CAPSLOCK, KeyProps.W: 1.75}]
|
||||
+ CommonLayout.letters_row_2()
|
||||
+ CommonKeys.letters_row_2()
|
||||
+ [{KeyProps.KEY: KEY_ENTER, KeyProps.W: 2.25}]
|
||||
),
|
||||
(
|
||||
@ -26,7 +28,7 @@ func rows() -> Array[Array]:
|
||||
KeyProps.LOC: KEY_LOCATION_LEFT
|
||||
}
|
||||
]
|
||||
+ CommonLayout.letters_row_3()
|
||||
+ CommonKeys.letters_row_3()
|
||||
+ [
|
||||
{
|
||||
KeyProps.KEY: KEY_SHIFT,
|
||||
@ -35,5 +37,5 @@ func rows() -> Array[Array]:
|
||||
}
|
||||
]
|
||||
),
|
||||
CommonLayout.bottom_row()
|
||||
CommonKeys.bottom_row()
|
||||
]
|
||||
|
||||
@ -1,32 +1,24 @@
|
||||
class_name LayoutANSIVariant extends AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
return "ANSI (big enter)"
|
||||
static var name := "ANSI (Big-ass Enter)"
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
func get_name() -> String:
|
||||
return name
|
||||
|
||||
|
||||
func get_key_dict_rows() -> Array[Array]:
|
||||
return [
|
||||
(
|
||||
CommonLayout.number_row()
|
||||
CommonKeys.number_row()
|
||||
+ [{KeyProps.KEY: KEY_BACKSLASH}, {KeyProps.KEY: KEY_BACKSPACE}]
|
||||
),
|
||||
(
|
||||
[{KeyProps.KEY: KEY_TAB, KeyProps.W: 1.5}]
|
||||
+ CommonLayout.letters_row_1()
|
||||
+ [
|
||||
{
|
||||
KeyProps.KEY: KEY_ENTER,
|
||||
KeyProps.W: 1.5,
|
||||
KeyProps.H: 2,
|
||||
KeyProps.SECONDARY_RECT: true,
|
||||
KeyProps.W2: 2.25,
|
||||
KeyProps.Y2: 1,
|
||||
KeyProps.X2: -0.75,
|
||||
}
|
||||
]
|
||||
+ CommonKeys.letters_row_1()
|
||||
+ [CommonKeys.big_enter_ansi()]
|
||||
),
|
||||
[{KeyProps.KEY: KEY_CAPSLOCK, KeyProps.W: 1.75}] + CommonLayout.letters_row_2(),
|
||||
[{KeyProps.KEY: KEY_CAPSLOCK, KeyProps.W: 1.75}] + CommonKeys.letters_row_2(),
|
||||
(
|
||||
[
|
||||
{
|
||||
@ -35,7 +27,7 @@ func rows() -> Array[Array]:
|
||||
KeyProps.LOC: KEY_LOCATION_LEFT
|
||||
}
|
||||
]
|
||||
+ CommonLayout.letters_row_3()
|
||||
+ CommonKeys.letters_row_3()
|
||||
+ [
|
||||
{
|
||||
KeyProps.KEY: KEY_SHIFT,
|
||||
@ -44,5 +36,5 @@ func rows() -> Array[Array]:
|
||||
}
|
||||
]
|
||||
),
|
||||
CommonLayout.bottom_row()
|
||||
CommonKeys.bottom_row()
|
||||
]
|
||||
|
||||
80
scripts/layouts/layout_custom.gd
Normal file
80
scripts/layouts/layout_custom.gd
Normal file
@ -0,0 +1,80 @@
|
||||
class_name LayoutCustom extends AbstractLayout
|
||||
|
||||
var failed: bool
|
||||
|
||||
var _name: String
|
||||
var _rows: Array[Array]
|
||||
var _file_name: String
|
||||
var _has_errors: bool
|
||||
|
||||
|
||||
func _init(path: String) -> void:
|
||||
_file_name = path.get_file()
|
||||
_name = _file_name
|
||||
|
||||
var data: Variant = _load_json_file(path)
|
||||
if not data:
|
||||
failed = true
|
||||
push_error("%s: COULD NOT PARSE CUSTOM LAYOUT JSON" % _file_name)
|
||||
print()
|
||||
return
|
||||
|
||||
var layout_rows := _deserialize(data, _file_name)
|
||||
if not layout_rows:
|
||||
failed = true
|
||||
push_error("%s: COULD NOT DESERIALIZE CUSTOM LAYOUT" % _file_name)
|
||||
print()
|
||||
return
|
||||
|
||||
if _has_errors:
|
||||
print()
|
||||
_rows = layout_rows
|
||||
|
||||
|
||||
func get_name() -> String:
|
||||
return _name
|
||||
|
||||
|
||||
func get_key_dict_rows() -> Array[Array]:
|
||||
return _rows
|
||||
|
||||
|
||||
func _load_json_file(path: String) -> Variant:
|
||||
var file := FileAccess.open(path, FileAccess.READ)
|
||||
if not file:
|
||||
var file_err := FileAccess.get_open_error()
|
||||
push_error("%s: error opening file: %s" % [_file_name, error_string(file_err)])
|
||||
return []
|
||||
|
||||
var content := file.get_as_text()
|
||||
var json := JSON.new()
|
||||
var err := json.parse(content)
|
||||
if err:
|
||||
push_error(
|
||||
(
|
||||
"%s: json parse error at line %s: %s"
|
||||
% [_file_name, json.get_error_line(), json.get_error_message()]
|
||||
)
|
||||
)
|
||||
return []
|
||||
|
||||
var data: Variant = json.data
|
||||
|
||||
return data
|
||||
|
||||
|
||||
func _deserialize(data: Variant, path: String) -> Array[Array]:
|
||||
var parser: AbstractParser
|
||||
if data is Array:
|
||||
parser = ParserKLE.new(data as Array, path)
|
||||
elif data is Dictionary:
|
||||
parser = ParserQMK.new(data as Dictionary, path)
|
||||
else:
|
||||
push_error(
|
||||
"%s: top-level json item is neither an array nor a dictionary" % _file_name
|
||||
)
|
||||
return []
|
||||
if parser.get_name() != "":
|
||||
_name = parser.get_name()
|
||||
_has_errors = parser.has_errors()
|
||||
return parser.get_rows()
|
||||
@ -1,31 +1,23 @@
|
||||
class_name LayoutISO extends AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
return "ISO"
|
||||
static var name := "ISO"
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
func get_name() -> String:
|
||||
return name
|
||||
|
||||
|
||||
func get_key_dict_rows() -> Array[Array]:
|
||||
return [
|
||||
CommonLayout.number_row() + [{KeyProps.KEY: KEY_BACKSPACE, KeyProps.W: 2}],
|
||||
CommonKeys.number_row() + [{KeyProps.KEY: KEY_BACKSPACE, KeyProps.W: 2}],
|
||||
(
|
||||
[{KeyProps.KEY: KEY_TAB, KeyProps.W: 1.5}]
|
||||
+ CommonLayout.letters_row_1()
|
||||
+ [
|
||||
{
|
||||
KeyProps.KEY: KEY_ENTER,
|
||||
KeyProps.W: 1.25,
|
||||
KeyProps.H: 2,
|
||||
KeyProps.X: 0.25,
|
||||
KeyProps.SECONDARY_RECT: true,
|
||||
KeyProps.W2: 1.5,
|
||||
KeyProps.X2: -0.25,
|
||||
}
|
||||
]
|
||||
+ CommonKeys.letters_row_1()
|
||||
+ [CommonKeys.big_enter_iso()]
|
||||
),
|
||||
(
|
||||
[{KeyProps.KEY: KEY_CAPSLOCK, KeyProps.W: 1.75}]
|
||||
+ CommonLayout.letters_row_2()
|
||||
+ CommonKeys.letters_row_2()
|
||||
+ [{KeyProps.KEY: KEY_BACKSLASH}]
|
||||
),
|
||||
(
|
||||
@ -37,7 +29,7 @@ func rows() -> Array[Array]:
|
||||
},
|
||||
{KeyProps.KEY: KEY_SECTION},
|
||||
]
|
||||
+ CommonLayout.letters_row_3()
|
||||
+ CommonKeys.letters_row_3()
|
||||
+ [
|
||||
{
|
||||
KeyProps.KEY: KEY_SHIFT,
|
||||
@ -46,5 +38,5 @@ func rows() -> Array[Array]:
|
||||
}
|
||||
]
|
||||
),
|
||||
CommonLayout.bottom_row()
|
||||
CommonKeys.bottom_row()
|
||||
]
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
class_name LayoutJD40 extends AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
return "JD40"
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
return [
|
||||
(
|
||||
[{KeyProps.KEY: KEY_ESCAPE}]
|
||||
+ CommonLayout.letters_1()
|
||||
+ [{KeyProps.KEY: KEY_BACKSPACE}]
|
||||
),
|
||||
(
|
||||
[{KeyProps.KEY: KEY_TAB, KeyProps.W: 1.5}]
|
||||
+ CommonLayout.letters_2()
|
||||
+ [{KeyProps.KEY: KEY_ENTER, KeyProps.W: 1.5}]
|
||||
),
|
||||
(
|
||||
[
|
||||
{
|
||||
KeyProps.KEY: KEY_SHIFT,
|
||||
KeyProps.W: 1.75,
|
||||
KeyProps.LOC: KEY_LOCATION_LEFT
|
||||
}
|
||||
]
|
||||
+ CommonLayout.letters_3()
|
||||
+ [
|
||||
{KeyProps.KEY: KEY_COMMA},
|
||||
{
|
||||
KeyProps.KEY: KEY_SHIFT,
|
||||
KeyProps.W: 1.25,
|
||||
KeyProps.LOC: KEY_LOCATION_RIGHT
|
||||
},
|
||||
{KeyProps.KEY: KEY_PERIOD},
|
||||
]
|
||||
),
|
||||
[
|
||||
{KeyProps.KEY: KEY_CTRL, KeyProps.W: 1.5, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_SPECIAL, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_ALT, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_SPACE, KeyProps.W: 6},
|
||||
{
|
||||
KeyProps.KEY: KEY_SPECIAL,
|
||||
KeyProps.W: 1.25,
|
||||
KeyProps.LOC: KEY_LOCATION_RIGHT
|
||||
},
|
||||
{
|
||||
KeyProps.KEY: KEY_CTRL,
|
||||
KeyProps.W: 1.25,
|
||||
KeyProps.LOC: KEY_LOCATION_RIGHT
|
||||
},
|
||||
]
|
||||
]
|
||||
@ -1 +0,0 @@
|
||||
uid://i3bve82hdaop
|
||||
@ -1,34 +1,26 @@
|
||||
class_name LayoutJIS extends AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
return "JIS"
|
||||
static var name := "JIS"
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
func get_name() -> String:
|
||||
return name
|
||||
|
||||
|
||||
func get_key_dict_rows() -> Array[Array]:
|
||||
return [
|
||||
(
|
||||
CommonLayout.number_row()
|
||||
+ [{KeyProps.KEY: KEY_UNKNOWN}, {KeyProps.KEY: KEY_BACKSPACE}]
|
||||
CommonKeys.number_row()
|
||||
+ [{KeyProps.KEY: KEY_NONE}, {KeyProps.KEY: KEY_BACKSPACE}]
|
||||
),
|
||||
(
|
||||
[{KeyProps.KEY: KEY_TAB, KeyProps.W: 1.5}]
|
||||
+ CommonLayout.letters_row_1()
|
||||
+ [
|
||||
{
|
||||
KeyProps.KEY: KEY_ENTER,
|
||||
KeyProps.W: 1.25,
|
||||
KeyProps.H: 2,
|
||||
KeyProps.X: 0.25,
|
||||
KeyProps.SECONDARY_RECT: true,
|
||||
KeyProps.W2: 1.5,
|
||||
KeyProps.X2: -0.25,
|
||||
}
|
||||
]
|
||||
+ CommonKeys.letters_row_1()
|
||||
+ [CommonKeys.big_enter_iso()]
|
||||
),
|
||||
(
|
||||
[{KeyProps.KEY: KEY_CAPSLOCK, KeyProps.W: 1.75}]
|
||||
+ CommonLayout.letters_row_2()
|
||||
+ CommonKeys.letters_row_2()
|
||||
+ [{KeyProps.KEY: KEY_BACKSLASH}]
|
||||
),
|
||||
(
|
||||
@ -39,9 +31,9 @@ func rows() -> Array[Array]:
|
||||
KeyProps.LOC: KEY_LOCATION_LEFT
|
||||
}
|
||||
]
|
||||
+ CommonLayout.letters_row_3()
|
||||
+ CommonKeys.letters_row_3()
|
||||
+ [
|
||||
{KeyProps.KEY: KEY_UNKNOWN},
|
||||
{KeyProps.KEY: KEY_NONE},
|
||||
{
|
||||
KeyProps.KEY: KEY_SHIFT,
|
||||
KeyProps.W: 1.75,
|
||||
@ -53,10 +45,10 @@ func rows() -> Array[Array]:
|
||||
{KeyProps.KEY: KEY_CTRL, KeyProps.W: 1.25, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_META, KeyProps.W: 1.25, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_ALT, KeyProps.W: 1.25, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_SPECIAL, KeyProps.W: 1.25},
|
||||
{KeyProps.KEY: KEY_NONE, KeyProps.W: 1.25},
|
||||
{KeyProps.KEY: KEY_SPACE, KeyProps.W: 2.5},
|
||||
{KeyProps.KEY: KEY_SPECIAL, KeyProps.W: 1.25},
|
||||
{KeyProps.KEY: KEY_SPECIAL, KeyProps.W: 1.25},
|
||||
{KeyProps.KEY: KEY_NONE, KeyProps.W: 1.25},
|
||||
{KeyProps.KEY: KEY_NONE, KeyProps.W: 1.25},
|
||||
{KeyProps.KEY: KEY_ALT, KeyProps.W: 1.25, KeyProps.LOC: KEY_LOCATION_RIGHT},
|
||||
{
|
||||
KeyProps.KEY: KEY_META,
|
||||
|
||||
@ -1,276 +0,0 @@
|
||||
class_name LayoutKLE extends AbstractLayout
|
||||
|
||||
const LABEL_TO_KEYCODE_MAP: Dictionary[String, Key] = {
|
||||
"`": KEY_QUOTELEFT,
|
||||
"-": KEY_MINUS,
|
||||
"=": KEY_EQUAL,
|
||||
"[": KEY_BRACKETLEFT,
|
||||
"]": KEY_BRACKETRIGHT,
|
||||
"\\": KEY_BACKSLASH,
|
||||
";": KEY_SEMICOLON,
|
||||
"'": KEY_APOSTROPHE,
|
||||
",": KEY_COMMA,
|
||||
".": KEY_PERIOD,
|
||||
"/": KEY_SLASH,
|
||||
"#": KEY_NUMBERSIGN,
|
||||
"Esc": KEY_ESCAPE,
|
||||
"Back Space": KEY_BACKSPACE,
|
||||
"Caps Lock": KEY_CAPSLOCK,
|
||||
"Win": KEY_META,
|
||||
"AltGr": KEY_ALT,
|
||||
"PrtSc": KEY_PRINT,
|
||||
"Scroll Lock": KEY_SCROLLLOCK,
|
||||
"PgUp": KEY_PAGEUP,
|
||||
"Page Up": KEY_PAGEUP,
|
||||
"PgDn": KEY_PAGEDOWN,
|
||||
"Page Down": KEY_PAGEDOWN,
|
||||
"Num Lock": KEY_NUMLOCK,
|
||||
"Fn": KEY_SPECIAL,
|
||||
"↑": KEY_UP,
|
||||
"←": KEY_LEFT,
|
||||
"↓": KEY_DOWN,
|
||||
"→": KEY_RIGHT,
|
||||
}
|
||||
|
||||
const LABEL_TO_NUMPAD_KEYCODE_MAP: Dictionary[String, Key] = {
|
||||
"/": KEY_KP_DIVIDE,
|
||||
"*": KEY_KP_MULTIPLY,
|
||||
"-": KEY_KP_SUBTRACT,
|
||||
"+": KEY_KP_ADD,
|
||||
".": KEY_KP_PERIOD,
|
||||
}
|
||||
|
||||
const W := "w"
|
||||
const H := "h"
|
||||
const X := "x"
|
||||
const Y := "y"
|
||||
|
||||
const W2 := "w2"
|
||||
const H2 := "h2"
|
||||
const X2 := "x2"
|
||||
const Y2 := "y2"
|
||||
|
||||
const KEY_DICT := "key_dict"
|
||||
const POS := "pos"
|
||||
|
||||
var has_errors: bool = false
|
||||
|
||||
var _name: String
|
||||
var _rows: Array[Array]
|
||||
|
||||
|
||||
func _init(json_path: String) -> void:
|
||||
var data := _load_json_file(json_path)
|
||||
if not data:
|
||||
has_errors = true
|
||||
printerr("ERROR LOADING KLE LAYOUT JSON: '%s'" % json_path)
|
||||
return
|
||||
|
||||
var layout_rows := _deserialize(data)
|
||||
if not layout_rows:
|
||||
has_errors = true
|
||||
printerr("ERROR DESERIALIZING KLE LAYOUT: '%s'" % json_path)
|
||||
return
|
||||
|
||||
_rows = layout_rows
|
||||
|
||||
|
||||
func name() -> String:
|
||||
return _name
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
return _rows
|
||||
|
||||
|
||||
func _load_json_file(path: String) -> Array:
|
||||
var file := FileAccess.open(path, FileAccess.READ)
|
||||
if not file:
|
||||
printerr("error opening file '%s': %s" % [path, FileAccess.get_open_error()])
|
||||
return []
|
||||
var content := file.get_as_text()
|
||||
|
||||
var json := JSON.new()
|
||||
var err := json.parse(content)
|
||||
if err != OK:
|
||||
printerr(
|
||||
(
|
||||
"json parse error in '%s' at line %s: %s"
|
||||
% [path, json.get_error_line(), json.get_error_message()]
|
||||
)
|
||||
)
|
||||
return []
|
||||
|
||||
var data: Variant = json.data
|
||||
if data is not Array:
|
||||
return []
|
||||
|
||||
_name = path.get_file()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
func _deserialize(data: Array) -> Array[Array]:
|
||||
var layout_rows: Array[Array] = []
|
||||
var key_pos_dicts: Dictionary[Key, Array] = {}
|
||||
|
||||
var row_index: int = 0
|
||||
for data_row: Variant in data:
|
||||
if data_row is Array:
|
||||
var layout_row := _deserialize_row(
|
||||
data_row as Array, key_pos_dicts, row_index
|
||||
)
|
||||
layout_rows.append(layout_row)
|
||||
row_index += 1
|
||||
|
||||
_get_key_locations(key_pos_dicts)
|
||||
|
||||
return layout_rows
|
||||
|
||||
|
||||
func _deserialize_row(
|
||||
data_row: Array, key_pos_dicts: Dictionary[Key, Array], row_index: int
|
||||
) -> Array[Dictionary]:
|
||||
var layout_row: Array[Dictionary] = []
|
||||
|
||||
var key_pos := Vector2(0, row_index)
|
||||
|
||||
var current_key_data_dict: Dictionary = {}
|
||||
for key_data: Variant in data_row:
|
||||
if key_data is Dictionary:
|
||||
current_key_data_dict = key_data as Dictionary
|
||||
|
||||
if key_data is String:
|
||||
var legend := (key_data as String).split("\n")
|
||||
var keycode := _get_keycode_from_legend(legend, current_key_data_dict)
|
||||
var key_dict := {KeyProps.KEY: keycode}
|
||||
key_dict.merge(_deserialize_key(current_key_data_dict))
|
||||
layout_row.append(key_dict)
|
||||
|
||||
key_pos.x += key_dict[KeyProps.X] if key_dict.has(KeyProps.X) else 0.0
|
||||
var key_pos_dict := {KEY_DICT: key_dict, POS: key_pos}
|
||||
if key_pos_dicts.has(keycode):
|
||||
key_pos_dicts[keycode].append(key_pos_dict)
|
||||
else:
|
||||
key_pos_dicts[keycode] = [key_pos_dict] as Array[Dictionary]
|
||||
key_pos.x += key_dict[KeyProps.W] if key_dict.has(KeyProps.W) else 1.0
|
||||
|
||||
current_key_data_dict = _cleanup_key_data_dict(current_key_data_dict)
|
||||
|
||||
return layout_row
|
||||
|
||||
|
||||
func _deserialize_key(data_key: Dictionary) -> Dictionary:
|
||||
var key_dict: Dictionary = {}
|
||||
|
||||
if data_key.has(W):
|
||||
key_dict[KeyProps.W] = data_key[W]
|
||||
if data_key.has(H):
|
||||
key_dict[KeyProps.H] = data_key[H]
|
||||
if data_key.has(X):
|
||||
key_dict[KeyProps.X] = data_key[X]
|
||||
if data_key.has(Y):
|
||||
key_dict[KeyProps.Y] = data_key[Y]
|
||||
|
||||
if data_key.has(W2):
|
||||
key_dict[KeyProps.W2] = data_key[W2]
|
||||
key_dict[KeyProps.SECONDARY_RECT] = true
|
||||
if data_key.has(H2):
|
||||
key_dict[KeyProps.H2] = data_key[H2]
|
||||
key_dict[KeyProps.SECONDARY_RECT] = true
|
||||
if data_key.has(X2):
|
||||
key_dict[KeyProps.X2] = data_key[X2]
|
||||
key_dict[KeyProps.SECONDARY_RECT] = true
|
||||
if data_key.has(Y2):
|
||||
key_dict[KeyProps.Y2] = data_key[Y2]
|
||||
key_dict[KeyProps.SECONDARY_RECT] = true
|
||||
|
||||
return key_dict
|
||||
|
||||
|
||||
func _cleanup_key_data_dict(data_key: Dictionary) -> Dictionary:
|
||||
data_key.erase(W)
|
||||
data_key.erase(H)
|
||||
data_key.erase(X)
|
||||
data_key.erase(Y)
|
||||
return data_key
|
||||
|
||||
|
||||
func _get_keycode_from_legend(legend: Array[String], data_key: Dictionary) -> Key:
|
||||
if legend.size() == 1 and legend[0] == "" and data_key.has(W) and data_key[W] > 1:
|
||||
return KEY_SPACE
|
||||
|
||||
var keycode := KEY_NONE
|
||||
|
||||
keycode = _get_numpad_keycode_from_legend(legend)
|
||||
|
||||
if keycode == KEY_NONE:
|
||||
keycode = OS.find_keycode_from_string(legend[0])
|
||||
|
||||
if keycode == KEY_NONE and legend.size() == 2:
|
||||
keycode = OS.find_keycode_from_string(legend[1])
|
||||
|
||||
if keycode == KEY_NONE and legend.size() == 2:
|
||||
if LABEL_TO_KEYCODE_MAP.has(legend[1]):
|
||||
keycode = LABEL_TO_KEYCODE_MAP[legend[1]]
|
||||
elif LABEL_TO_KEYCODE_MAP.has(legend[0]):
|
||||
keycode = LABEL_TO_KEYCODE_MAP[legend[0]]
|
||||
|
||||
if (
|
||||
keycode == KEY_NONE
|
||||
and legend.size() == 1
|
||||
and LABEL_TO_KEYCODE_MAP.has(legend[0])
|
||||
):
|
||||
keycode = LABEL_TO_KEYCODE_MAP[legend[0]]
|
||||
|
||||
if keycode == KEY_NONE:
|
||||
printerr("%s: could not recognize key label %s" % [_name, str(legend)])
|
||||
|
||||
return keycode
|
||||
|
||||
|
||||
func _get_numpad_keycode_from_legend(legend: Array[String]) -> Key:
|
||||
if legend.size() == 1 or legend.size() == 2:
|
||||
if legend[0].length() == 1 and legend[0].is_valid_int():
|
||||
return KEY_KP_0 + int(legend[0]) as Key
|
||||
if (
|
||||
LABEL_TO_NUMPAD_KEYCODE_MAP.has(legend[0])
|
||||
and not (legend.size() == 2 and legend[1].length() == 1)
|
||||
):
|
||||
return LABEL_TO_NUMPAD_KEYCODE_MAP[legend[0]]
|
||||
|
||||
return KEY_NONE
|
||||
|
||||
|
||||
func _get_key_locations(key_pos_dicts: Dictionary[Key, Array]) -> void:
|
||||
for keycode in key_pos_dicts:
|
||||
var dicts := key_pos_dicts[keycode] as Array[Dictionary]
|
||||
if dicts.size() == 1:
|
||||
continue
|
||||
|
||||
var key_pos_dict_left: Dictionary
|
||||
var key_pos_dict_right: Dictionary
|
||||
for key_pos_dict in dicts:
|
||||
var key_pos := key_pos_dict[POS] as Vector2
|
||||
if (
|
||||
not key_pos_dict_left
|
||||
or key_pos.x < (key_pos_dict_left[POS] as Vector2).x
|
||||
):
|
||||
key_pos_dict_left = key_pos_dict
|
||||
continue
|
||||
if (
|
||||
not key_pos_dict_right
|
||||
or key_pos.x > (key_pos_dict_right[POS] as Vector2).x
|
||||
):
|
||||
key_pos_dict_right = key_pos_dict
|
||||
continue
|
||||
|
||||
if key_pos_dict_left == key_pos_dict_right:
|
||||
continue
|
||||
|
||||
if keycode == KEY_ENTER:
|
||||
key_pos_dict_right[KEY_DICT][KeyProps.KEY] = KEY_KP_ENTER
|
||||
continue
|
||||
|
||||
key_pos_dict_left[KEY_DICT][KeyProps.LOC] = KEY_LOCATION_LEFT
|
||||
key_pos_dict_right[KEY_DICT][KeyProps.LOC] = KEY_LOCATION_RIGHT
|
||||
@ -1,34 +0,0 @@
|
||||
class_name LayoutPlanck extends AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
return "Planck"
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
return [
|
||||
(
|
||||
[{KeyProps.KEY: KEY_TAB}]
|
||||
+ CommonLayout.letters_1()
|
||||
+ [{KeyProps.KEY: KEY_BACKSPACE}]
|
||||
),
|
||||
[{KeyProps.KEY: KEY_ESCAPE}] + CommonLayout.letters_row_2(),
|
||||
(
|
||||
[{KeyProps.KEY: KEY_SHIFT, KeyProps.LOC: KEY_LOCATION_LEFT}]
|
||||
+ CommonLayout.letters_row_3()
|
||||
+ [{KeyProps.KEY: KEY_ENTER}]
|
||||
),
|
||||
[
|
||||
{KeyProps.KEY: KEY_SPECIAL},
|
||||
{KeyProps.KEY: KEY_CTRL, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_ALT, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_META, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_SPECIAL, KeyProps.LOC: KEY_LOCATION_LEFT},
|
||||
{KeyProps.KEY: KEY_SPACE, KeyProps.W: 2},
|
||||
{KeyProps.KEY: KEY_SPECIAL, KeyProps.LOC: KEY_LOCATION_RIGHT},
|
||||
{KeyProps.KEY: KEY_LEFT},
|
||||
{KeyProps.KEY: KEY_DOWN},
|
||||
{KeyProps.KEY: KEY_UP},
|
||||
{KeyProps.KEY: KEY_RIGHT},
|
||||
]
|
||||
]
|
||||
@ -1 +0,0 @@
|
||||
uid://dqjuvbt87uah0
|
||||
@ -1,9 +1,14 @@
|
||||
class_name LayoutTest extends AbstractLayout
|
||||
|
||||
|
||||
static func name_static() -> String:
|
||||
return "test"
|
||||
static var name := "test"
|
||||
|
||||
|
||||
func rows() -> Array[Array]:
|
||||
return [[{KeyProps.KEY: KEY_SPECIAL}]]
|
||||
func get_name() -> String:
|
||||
return name
|
||||
|
||||
|
||||
func get_key_dict_rows() -> Array[Array]:
|
||||
return [
|
||||
[{KeyProps.KEY: KEY_Q}, {KeyProps.KEY: KEY_W}],
|
||||
[{KeyProps.KEY: KEY_A}, {KeyProps.KEY: KEY_S}],
|
||||
]
|
||||
|
||||
326
scripts/layouts/parser_kle.gd
Normal file
326
scripts/layouts/parser_kle.gd
Normal file
@ -0,0 +1,326 @@
|
||||
class_name ParserKLE extends AbstractParser
|
||||
|
||||
const LABEL_TO_KEYCODE_MAP: Dictionary[String, Key] = {
|
||||
"`": KEY_QUOTELEFT,
|
||||
"-": KEY_MINUS,
|
||||
"=": KEY_EQUAL,
|
||||
"[": KEY_BRACKETLEFT,
|
||||
"]": KEY_BRACKETRIGHT,
|
||||
"\\": KEY_BACKSLASH,
|
||||
";": KEY_SEMICOLON,
|
||||
"'": KEY_APOSTROPHE,
|
||||
",": KEY_COMMA,
|
||||
".": KEY_PERIOD,
|
||||
"/": KEY_SLASH,
|
||||
"#": KEY_NUMBERSIGN,
|
||||
"Return": KEY_ENTER,
|
||||
"Esc": KEY_ESCAPE,
|
||||
"esc": KEY_ESCAPE,
|
||||
"Bksp": KEY_BACKSPACE,
|
||||
"Back Space": KEY_BACKSPACE,
|
||||
"Back<br>Space": KEY_BACKSPACE,
|
||||
"Caps Lock": KEY_CAPSLOCK,
|
||||
"Caps<br>Lock": KEY_CAPSLOCK,
|
||||
"caps lock": KEY_CAPSLOCK,
|
||||
"control": KEY_CTRL,
|
||||
"Win": KEY_META,
|
||||
"Cmd": KEY_META,
|
||||
"super": KEY_META,
|
||||
"⌘": KEY_META,
|
||||
"command": KEY_META,
|
||||
"Meta": KEY_META,
|
||||
"AltGr": KEY_ALT,
|
||||
"PrtSc": KEY_PRINT,
|
||||
"PrintScr SysReq": KEY_PRINT,
|
||||
"Scroll Lock": KEY_SCROLLLOCK,
|
||||
"Scroll<br>lock": KEY_SCROLLLOCK,
|
||||
"PgUp": KEY_PAGEUP,
|
||||
"Page Up": KEY_PAGEUP,
|
||||
"Page<br>Up": KEY_PAGEUP,
|
||||
"PgDn": KEY_PAGEDOWN,
|
||||
"Page<br>Down": KEY_PAGEDOWN,
|
||||
"Pause Break": KEY_PAUSE,
|
||||
"Page Down": KEY_PAGEDOWN,
|
||||
"Num Lock": KEY_NUMLOCK,
|
||||
"↑": KEY_UP,
|
||||
"⇧": KEY_UP,
|
||||
"↑": KEY_UP,
|
||||
"←": KEY_LEFT,
|
||||
"⇦": KEY_LEFT,
|
||||
"←": KEY_LEFT,
|
||||
"↓": KEY_DOWN,
|
||||
"⇩": KEY_DOWN,
|
||||
"↓": KEY_DOWN,
|
||||
"→": KEY_RIGHT,
|
||||
"⇨": KEY_RIGHT,
|
||||
"→": KEY_RIGHT,
|
||||
}
|
||||
|
||||
const LABEL_TO_NUMPAD_KEYCODE_MAP: Dictionary[String, Key] = {
|
||||
"/": KEY_KP_DIVIDE,
|
||||
"*": KEY_KP_MULTIPLY,
|
||||
"-": KEY_KP_SUBTRACT,
|
||||
"+": KEY_KP_ADD,
|
||||
".": KEY_KP_PERIOD,
|
||||
}
|
||||
|
||||
const NAME := "name"
|
||||
|
||||
const W := "w"
|
||||
const H := "h"
|
||||
const X := "x"
|
||||
const Y := "y"
|
||||
|
||||
const W2 := "w2"
|
||||
const H2 := "h2"
|
||||
const X2 := "x2"
|
||||
const Y2 := "y2"
|
||||
|
||||
const R := "r"
|
||||
const RX := "rx"
|
||||
const RY := "ry"
|
||||
|
||||
const N := "n"
|
||||
|
||||
const KEY_DICT := "key_dict"
|
||||
const POS := "pos"
|
||||
|
||||
var _name: String
|
||||
var _rows: Array[Array]
|
||||
var _file_name: String
|
||||
var _has_errors: bool
|
||||
|
||||
|
||||
func _init(data: Array, file_name: String) -> void:
|
||||
_file_name = file_name
|
||||
|
||||
var key_pos_dicts: Dictionary[Key, Array] = {}
|
||||
var pos: Vector2 = Vector2.ZERO
|
||||
var pivot := Vector2.ZERO
|
||||
var angle: float = 0
|
||||
|
||||
for data_row: Variant in data:
|
||||
if data_row is Dictionary and (data_row as Dictionary).has(NAME):
|
||||
_name = (data_row as Dictionary)[NAME]
|
||||
|
||||
if data_row is Array:
|
||||
var result := _deserialize_row(
|
||||
data_row as Array, key_pos_dicts, pos, pivot, angle
|
||||
)
|
||||
var layout_row := result[0] as Array[Dictionary]
|
||||
if layout_row:
|
||||
_rows.append(layout_row)
|
||||
pos = result[1]
|
||||
pos.y += 1
|
||||
pivot = result[2]
|
||||
angle = result[3]
|
||||
|
||||
_get_key_locations(key_pos_dicts)
|
||||
|
||||
|
||||
func get_name() -> String:
|
||||
return _name
|
||||
|
||||
|
||||
func get_rows() -> Array[Array]:
|
||||
return _rows
|
||||
|
||||
|
||||
func has_errors() -> bool:
|
||||
return _has_errors
|
||||
|
||||
|
||||
func _deserialize_row(
|
||||
data_row: Array,
|
||||
key_pos_dicts: Dictionary[Key, Array],
|
||||
pos: Vector2,
|
||||
pivot: Vector2,
|
||||
angle: float,
|
||||
) -> Array:
|
||||
var layout_row: Array[Dictionary] = []
|
||||
|
||||
pos.x = pivot.x
|
||||
|
||||
var data_key: Dictionary = {}
|
||||
var prev_keycode: Key = KEY_NONE
|
||||
for item: Variant in data_row:
|
||||
if item is Dictionary:
|
||||
data_key = item as Dictionary
|
||||
|
||||
if item is String:
|
||||
var legend := (item as String).split("\n")
|
||||
var keycode := _get_keycode_from_legend(legend, data_key, prev_keycode)
|
||||
var key_dict := {KeyProps.KEY: keycode}
|
||||
key_dict.merge(_deserialize_key(data_key))
|
||||
layout_row.append(key_dict)
|
||||
|
||||
if data_key.has(R):
|
||||
angle = -deg_to_rad(data_key[R] as float)
|
||||
if data_key.has(RX):
|
||||
pivot.x = data_key[RX]
|
||||
if data_key.has(RY):
|
||||
pivot.y = data_key[RY]
|
||||
if data_key.has(RX) or data_key.has(RY):
|
||||
pos = pivot
|
||||
|
||||
pos.x += data_key[X] if data_key.has(X) else 0.0
|
||||
pos.y += data_key[Y] if data_key.has(Y) else 0.0
|
||||
|
||||
var key_pos_rotated := KeyHelper.get_rotated_key_pos(
|
||||
Vector3(pos.x, 0, pos.y), pivot, angle
|
||||
)
|
||||
var key_pos_dict := {
|
||||
KEY_DICT: key_dict, POS: Vector2(key_pos_rotated.x, key_pos_rotated.z)
|
||||
}
|
||||
|
||||
if key_pos_dicts.has(keycode):
|
||||
key_pos_dicts[keycode].append(key_pos_dict)
|
||||
else:
|
||||
key_pos_dicts[keycode] = [key_pos_dict] as Array[Dictionary]
|
||||
pos.x += data_key[W] if data_key.has(W) else 1.0
|
||||
|
||||
data_key = {}
|
||||
prev_keycode = keycode
|
||||
|
||||
return [layout_row, pos, pivot, angle]
|
||||
|
||||
|
||||
func _deserialize_key(data_key: Dictionary) -> Dictionary:
|
||||
var key_dict: Dictionary = {}
|
||||
|
||||
if data_key.has(W):
|
||||
key_dict[KeyProps.W] = data_key[W]
|
||||
if data_key.has(H):
|
||||
key_dict[KeyProps.H] = data_key[H]
|
||||
if data_key.has(X):
|
||||
key_dict[KeyProps.X] = data_key[X]
|
||||
if data_key.has(Y):
|
||||
key_dict[KeyProps.Y] = data_key[Y]
|
||||
|
||||
if data_key.has(W2):
|
||||
key_dict[KeyProps.W2] = data_key[W2]
|
||||
if data_key.has(H2):
|
||||
key_dict[KeyProps.H2] = data_key[H2]
|
||||
if data_key.has(X2):
|
||||
key_dict[KeyProps.X2] = data_key[X2]
|
||||
if data_key.has(Y2):
|
||||
key_dict[KeyProps.Y2] = data_key[Y2]
|
||||
|
||||
if data_key.has(R):
|
||||
key_dict[KeyProps.R] = data_key[R]
|
||||
if data_key.has(RX):
|
||||
key_dict[KeyProps.PX] = data_key[RX]
|
||||
if data_key.has(RY):
|
||||
key_dict[KeyProps.PY] = data_key[RY]
|
||||
|
||||
if data_key.has(N):
|
||||
key_dict[KeyProps.NUB] = data_key[N]
|
||||
|
||||
return key_dict
|
||||
|
||||
|
||||
func _get_keycode_from_legend(
|
||||
legend: Array[String], data_key: Dictionary, prev_keycode: Key
|
||||
) -> Key:
|
||||
if legend.size() == 1 and legend[0] == "" and data_key.has(W) and data_key[W] > 1:
|
||||
return KEY_SPACE
|
||||
|
||||
var keycode := KEY_NONE
|
||||
|
||||
keycode = _get_numpad_keycode_from_legend(legend, prev_keycode)
|
||||
|
||||
if keycode == KEY_NONE:
|
||||
keycode = OS.find_keycode_from_string(legend[0])
|
||||
|
||||
if keycode == KEY_NONE and legend.size() >= 2:
|
||||
keycode = OS.find_keycode_from_string(legend[1])
|
||||
|
||||
if keycode == KEY_NONE and legend.size() >= 2:
|
||||
if LABEL_TO_KEYCODE_MAP.has(legend[1]):
|
||||
keycode = LABEL_TO_KEYCODE_MAP[legend[1]]
|
||||
elif LABEL_TO_KEYCODE_MAP.has(legend[0]):
|
||||
keycode = LABEL_TO_KEYCODE_MAP[legend[0]]
|
||||
|
||||
if (
|
||||
keycode == KEY_NONE
|
||||
and legend.size() == 1
|
||||
and LABEL_TO_KEYCODE_MAP.has(legend[0])
|
||||
):
|
||||
keycode = LABEL_TO_KEYCODE_MAP[legend[0]]
|
||||
|
||||
if keycode == KEY_NONE and legend.size() > 2:
|
||||
for i in range(2, legend.size()):
|
||||
if not legend[i]:
|
||||
continue
|
||||
keycode = OS.find_keycode_from_string(legend[i])
|
||||
if keycode == KEY_NONE and LABEL_TO_KEYCODE_MAP.has(legend[i]):
|
||||
keycode = LABEL_TO_KEYCODE_MAP[legend[i]]
|
||||
if keycode != KEY_NONE:
|
||||
break
|
||||
|
||||
if keycode == KEY_NONE:
|
||||
push_warning("%s: could not recognize key label %s" % [_file_name, str(legend)])
|
||||
_has_errors = true
|
||||
|
||||
return keycode
|
||||
|
||||
|
||||
func _get_numpad_keycode_from_legend(legend: Array[String], prev_keycode: Key) -> Key:
|
||||
if legend.size() == 1 or legend.size() >= 2:
|
||||
if legend[0].length() == 1 and legend[0].is_valid_int():
|
||||
return KEY_KP_0 + int(legend[0]) as Key
|
||||
if (
|
||||
LABEL_TO_NUMPAD_KEYCODE_MAP.has(legend[0])
|
||||
and not (legend.size() >= 2 and legend[1].length() == 1)
|
||||
):
|
||||
return LABEL_TO_NUMPAD_KEYCODE_MAP[legend[0]]
|
||||
|
||||
for label in legend:
|
||||
if OS.find_keycode_from_string(label) == KEY_ENTER:
|
||||
if prev_keycode >= KEY_KP_MULTIPLY and prev_keycode <= KEY_KP_9:
|
||||
return KEY_KP_ENTER
|
||||
break
|
||||
|
||||
return KEY_NONE
|
||||
|
||||
|
||||
func _get_key_locations(key_pos_dicts: Dictionary[Key, Array]) -> void:
|
||||
for keycode in key_pos_dicts:
|
||||
var dicts := key_pos_dicts[keycode] as Array[Dictionary]
|
||||
|
||||
if (
|
||||
keycode == KEY_NUMBERSIGN
|
||||
and key_pos_dicts.has(KEY_BACKSLASH)
|
||||
and key_pos_dicts[KEY_BACKSLASH][0][POS].x < dicts[0][POS].x
|
||||
):
|
||||
dicts[0][KEY_DICT][KeyProps.KEY] = KEY_BACKSLASH
|
||||
key_pos_dicts[KEY_BACKSLASH][0][KEY_DICT][KeyProps.KEY] = KEY_SECTION
|
||||
continue
|
||||
|
||||
if keycode == KEY_NONE or dicts.size() == 1:
|
||||
continue
|
||||
|
||||
var key_pos_dict_left: Dictionary
|
||||
var key_pos_dict_right: Dictionary
|
||||
for key_pos_dict in dicts:
|
||||
var key_pos := key_pos_dict[POS] as Vector2
|
||||
if (
|
||||
not key_pos_dict_left
|
||||
or key_pos.x < (key_pos_dict_left[POS] as Vector2).x
|
||||
):
|
||||
key_pos_dict_left = key_pos_dict
|
||||
if (
|
||||
not key_pos_dict_right
|
||||
or key_pos.x > (key_pos_dict_right[POS] as Vector2).x
|
||||
):
|
||||
key_pos_dict_right = key_pos_dict
|
||||
|
||||
if key_pos_dict_left == key_pos_dict_right:
|
||||
continue
|
||||
|
||||
if keycode == KEY_BACKSLASH:
|
||||
key_pos_dict_left[KEY_DICT][KeyProps.KEY] = KEY_SECTION
|
||||
continue
|
||||
|
||||
key_pos_dict_left[KEY_DICT][KeyProps.LOC] = KEY_LOCATION_LEFT
|
||||
key_pos_dict_right[KEY_DICT][KeyProps.LOC] = KEY_LOCATION_RIGHT
|
||||
1
scripts/layouts/parser_kle.gd.uid
Normal file
1
scripts/layouts/parser_kle.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dssjdkxq56jl8
|
||||
389
scripts/layouts/parser_qmk.gd
Normal file
389
scripts/layouts/parser_qmk.gd
Normal file
@ -0,0 +1,389 @@
|
||||
class_name ParserQMK extends AbstractParser
|
||||
|
||||
const KEYCODE_MAP: Dictionary[String, Key] = {
|
||||
"ENT": KEY_ENTER,
|
||||
"ESC": KEY_ESCAPE,
|
||||
"BSPC": KEY_BACKSPACE,
|
||||
"SPC": KEY_SPACE,
|
||||
"MINS": KEY_MINUS,
|
||||
"EQL": KEY_EQUAL,
|
||||
"LEFT_BRACKET": KEY_BRACKETLEFT,
|
||||
"LBRC": KEY_BRACKETLEFT,
|
||||
"RIGHT_BRACKET": KEY_BRACKETRIGHT,
|
||||
"RBRC": KEY_BRACKETRIGHT,
|
||||
"BSLS": KEY_BACKSLASH,
|
||||
"NONUS_HASH": KEY_BACKSLASH,
|
||||
"NUHS": KEY_BACKSLASH,
|
||||
"SCLN": KEY_SEMICOLON,
|
||||
"QUOTE": KEY_APOSTROPHE,
|
||||
"QUOT": KEY_APOSTROPHE,
|
||||
"GRAVE": KEY_QUOTELEFT,
|
||||
"GRV": KEY_QUOTELEFT,
|
||||
"COMM": KEY_COMMA,
|
||||
"DOT": KEY_PERIOD,
|
||||
"SLSH": KEY_SLASH,
|
||||
"NONUS_BACKSLASH": KEY_SECTION,
|
||||
"NUBS": KEY_SECTION,
|
||||
"CAPS_LOCK": KEY_CAPSLOCK,
|
||||
"CAPS": KEY_CAPSLOCK,
|
||||
"SCROLL_LOCK": KEY_SCROLLLOCK,
|
||||
"SCRL": KEY_SCROLLLOCK,
|
||||
"BRMD": KEY_SCROLLLOCK,
|
||||
"NUM_LOCK": KEY_NUMLOCK,
|
||||
"NUM": KEY_NUMLOCK,
|
||||
"PRINT_SCREEN": KEY_PRINT,
|
||||
"PSCR": KEY_PRINT,
|
||||
"PAUS": KEY_PAUSE,
|
||||
"BRK": KEY_PAUSE,
|
||||
"BRMU": KEY_PAUSE,
|
||||
"INS": KEY_INSERT,
|
||||
"PAGE_UP": KEY_PAGEUP,
|
||||
"PGUP": KEY_PAGEUP,
|
||||
"DEL": KEY_DELETE,
|
||||
"PAGE_DOWN": KEY_PAGEDOWN,
|
||||
"PGDN": KEY_PAGEDOWN,
|
||||
"RGHT": KEY_RIGHT,
|
||||
"APPLICATION": KEY_MENU,
|
||||
"APP": KEY_MENU,
|
||||
"SYSTEM_REQUEST": KEY_SYSREQ,
|
||||
"SYRQ": KEY_SYSREQ,
|
||||
"CLR": KEY_CLEAR,
|
||||
"RETURN": KEY_ENTER,
|
||||
"RETN": KEY_ENTER,
|
||||
"AUDIO_MUTE": KEY_VOLUMEMUTE,
|
||||
"MUTE": KEY_VOLUMEMUTE,
|
||||
"AUDIO_VOL_UP": KEY_VOLUMEUP,
|
||||
"VOLU": KEY_VOLUMEUP,
|
||||
"AUDIO_VOL_DOWN": KEY_VOLUMEDOWN,
|
||||
"VOLD": KEY_VOLUMEDOWN,
|
||||
"MEDIA_NEXT_TRACK": KEY_MEDIANEXT,
|
||||
"MNXT": KEY_MEDIANEXT,
|
||||
"MEDIA_PREV_TRACK": KEY_MEDIAPREVIOUS,
|
||||
"MPRV": KEY_MEDIAPREVIOUS,
|
||||
"MEDIA_STOP": KEY_MEDIASTOP,
|
||||
"MSTP": KEY_MEDIASTOP,
|
||||
"MEDIA_PLAY_PAUSE": KEY_MEDIAPLAY,
|
||||
"MPLY": KEY_MEDIAPLAY,
|
||||
"MAIL": KEY_LAUNCHMAIL,
|
||||
"WWW_SEARCH": KEY_SEARCH,
|
||||
"WSCH": KEY_SEARCH,
|
||||
"WWW_HOME": KEY_SEARCH,
|
||||
"WHOM": KEY_SEARCH,
|
||||
"WWW_BACK": KEY_BACK,
|
||||
"WBAK": KEY_BACK,
|
||||
"WWW_FORWARD": KEY_FORWARD,
|
||||
"WFWD": KEY_FORWARD,
|
||||
"WWW_STOP": KEY_STOP,
|
||||
"WSTP": KEY_STOP,
|
||||
"WWW_REFRESH": KEY_REFRESH,
|
||||
"WREF": KEY_REFRESH,
|
||||
"WWW_FAVORITES": KEY_FAVORITES,
|
||||
"WFAV": KEY_FAVORITES,
|
||||
"MEDIA_FAST_FORWARD": KEY_MEDIANEXT,
|
||||
"MFFD": KEY_MEDIANEXT,
|
||||
"MEDIA_REWIND": KEY_MEDIAPREVIOUS,
|
||||
"MRWD": KEY_MEDIAPREVIOUS,
|
||||
"INTERNATIONAL_1": KEY_BACKSLASH,
|
||||
"INT1": KEY_BACKSLASH,
|
||||
"INTERNATIONAL_3": KEY_YEN,
|
||||
"INT3": KEY_YEN,
|
||||
"KP_SLASH": KEY_KP_DIVIDE,
|
||||
"PSLS": KEY_KP_DIVIDE,
|
||||
"KP_ASTERISK": KEY_KP_MULTIPLY,
|
||||
"PAST": KEY_KP_MULTIPLY,
|
||||
"KP_MINUS": KEY_KP_SUBTRACT,
|
||||
"PMNS": KEY_KP_SUBTRACT,
|
||||
"KP_PLUS": KEY_KP_ADD,
|
||||
"PPLS": KEY_KP_ADD,
|
||||
"KP_ENTER": KEY_KP_ENTER,
|
||||
"PENT": KEY_KP_ENTER,
|
||||
"KP_1": KEY_KP_1,
|
||||
"P1": KEY_KP_1,
|
||||
"KP_2": KEY_KP_2,
|
||||
"P2": KEY_KP_2,
|
||||
"KP_3": KEY_KP_3,
|
||||
"P3": KEY_KP_3,
|
||||
"KP_4": KEY_KP_4,
|
||||
"P4": KEY_KP_4,
|
||||
"KP_5": KEY_KP_5,
|
||||
"P5": KEY_KP_5,
|
||||
"KP_6": KEY_KP_6,
|
||||
"P6": KEY_KP_6,
|
||||
"KP_7": KEY_KP_7,
|
||||
"P7": KEY_KP_7,
|
||||
"KP_8": KEY_KP_8,
|
||||
"P8": KEY_KP_8,
|
||||
"KP_9": KEY_KP_9,
|
||||
"P9": KEY_KP_9,
|
||||
"KP_0": KEY_KP_0,
|
||||
"P0": KEY_KP_0,
|
||||
"KP_DOT": KEY_KP_PERIOD,
|
||||
"PDOT": KEY_KP_PERIOD,
|
||||
"KP_COMMA": KEY_KP_PERIOD,
|
||||
"PCMM": KEY_KP_PERIOD,
|
||||
}
|
||||
|
||||
const KEYCODE_MODIFIER_LEFT_MAP: Dictionary[String, Key] = {
|
||||
"LEFT_CTRL": KEY_CTRL,
|
||||
"LCTL": KEY_CTRL,
|
||||
"LEFT_SHIFT": KEY_SHIFT,
|
||||
"LSFT": KEY_SHIFT,
|
||||
"LEFT_ALT": KEY_ALT,
|
||||
"LALT": KEY_ALT,
|
||||
"LOPT": KEY_ALT,
|
||||
"LEFT_GUI": KEY_META,
|
||||
"LGUI": KEY_META,
|
||||
"LCMD": KEY_META,
|
||||
"LWIN": KEY_META,
|
||||
}
|
||||
|
||||
const KEYCODE_MODIFIER_RIGHT_MAP: Dictionary[String, Key] = {
|
||||
"RIGHT_CTRL": KEY_CTRL,
|
||||
"RCTL": KEY_CTRL,
|
||||
"RIGHT_SHIFT": KEY_SHIFT,
|
||||
"RSFT": KEY_SHIFT,
|
||||
"RIGHT_ALT": KEY_ALT,
|
||||
"RALT": KEY_ALT,
|
||||
"ROPT": KEY_ALT,
|
||||
"RIGHT_GUI": KEY_META,
|
||||
"RGUI": KEY_META,
|
||||
"RCMD": KEY_META,
|
||||
"RWIN": KEY_META,
|
||||
}
|
||||
|
||||
const KEYBOARD_NAME := "keyboard_name"
|
||||
const LAYOUTS := "layouts"
|
||||
const LAYOUT := "layout"
|
||||
|
||||
const W := "w"
|
||||
const H := "h"
|
||||
const X := "x"
|
||||
const Y := "y"
|
||||
|
||||
const R := "r"
|
||||
const RX := "rx"
|
||||
const RY := "ry"
|
||||
|
||||
const KEYCODE := "keycode"
|
||||
|
||||
var _name: String
|
||||
var _rows: Array[Array]
|
||||
var _file_name: String
|
||||
var _has_errors: bool
|
||||
|
||||
|
||||
func _init(data: Dictionary, file_name: String) -> void:
|
||||
_file_name = file_name
|
||||
if data.has(KEYBOARD_NAME) and data[KEYBOARD_NAME] is String:
|
||||
_name = data[KEYBOARD_NAME]
|
||||
|
||||
if (
|
||||
not data.has(LAYOUTS)
|
||||
or data[LAYOUTS] is not Dictionary
|
||||
or (data[LAYOUTS] as Dictionary).size() == 0
|
||||
):
|
||||
push_error("%s: '%s' is missing" % [_file_name, LAYOUTS])
|
||||
_has_errors = true
|
||||
return
|
||||
|
||||
var layout_name := (data[LAYOUTS] as Dictionary).keys()[0] as String
|
||||
|
||||
if (
|
||||
data[LAYOUTS][layout_name] is not Dictionary
|
||||
or not (data[LAYOUTS][layout_name] as Dictionary).has(LAYOUT)
|
||||
or data[LAYOUTS][layout_name][LAYOUT] is not Array
|
||||
):
|
||||
push_error(
|
||||
"%s: '%s.%s.%s' is missing" % [_file_name, LAYOUTS, layout_name, LAYOUT]
|
||||
)
|
||||
_has_errors = true
|
||||
return
|
||||
|
||||
var data_keys := data[LAYOUTS][layout_name][LAYOUT] as Array
|
||||
var err := _get_keymap_keys(data_keys, file_name)
|
||||
if err:
|
||||
_has_errors = true
|
||||
return
|
||||
_rows = _deserialize_keys(data_keys)
|
||||
|
||||
|
||||
func get_name() -> String:
|
||||
return _name
|
||||
|
||||
|
||||
func get_rows() -> Array[Array]:
|
||||
return _rows
|
||||
|
||||
|
||||
func has_errors() -> bool:
|
||||
return _has_errors
|
||||
|
||||
|
||||
func _sort_data_keys(a: Variant, b: Variant) -> bool:
|
||||
if a is not Dictionary or b is not Dictionary:
|
||||
return false
|
||||
var a_dict := a as Dictionary
|
||||
var b_dict := b as Dictionary
|
||||
if not a_dict.has(Y) or not b_dict.has(Y):
|
||||
return false
|
||||
var a_y := a_dict[Y] as float
|
||||
var b_y := b_dict[Y] as float
|
||||
if a_y == b_y and a_dict.has(X) and a_dict.has(X):
|
||||
return (a_dict[X] as float) < (b_dict[X] as float)
|
||||
return a_y < b_y
|
||||
|
||||
|
||||
func _deserialize_keys(data_keys: Array) -> Array[Array]:
|
||||
data_keys.sort_custom(_sort_data_keys)
|
||||
var layout_rows: Array[Array] = []
|
||||
var layout_row: Array[Dictionary] = []
|
||||
var prev_pos_x: float = 0
|
||||
var prev_pos_y: float = 0
|
||||
|
||||
for i in range(data_keys.size()):
|
||||
if data_keys[i] is not Dictionary:
|
||||
continue
|
||||
var data_key := data_keys[i] as Dictionary
|
||||
if not data_key.has(X) or not data_key.has(Y):
|
||||
continue
|
||||
|
||||
if prev_pos_y != data_key[Y]:
|
||||
prev_pos_x = 0
|
||||
prev_pos_y += 1
|
||||
if layout_row:
|
||||
layout_rows.append(layout_row)
|
||||
layout_row = []
|
||||
|
||||
var keycode := (
|
||||
_get_keycode_from_keymap_key(data_key[KEYCODE] as String)
|
||||
if data_key.has(KEYCODE)
|
||||
else KEY_NONE
|
||||
)
|
||||
var key_dict := {KeyProps.KEY: keycode}
|
||||
(
|
||||
key_dict
|
||||
. merge(
|
||||
_deserialize_key(
|
||||
data_key,
|
||||
data_key[X] as float - prev_pos_x,
|
||||
data_key[Y] as float - prev_pos_y,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
var location := (
|
||||
_get_key_location(data_key[KEYCODE] as String)
|
||||
if data_key.has(KEYCODE)
|
||||
else KEY_LOCATION_UNSPECIFIED
|
||||
)
|
||||
if location != KEY_LOCATION_UNSPECIFIED:
|
||||
key_dict[KeyProps.LOC] = location
|
||||
|
||||
layout_row.append(key_dict)
|
||||
|
||||
var width: float = data_key[W] if data_key.has(W) else 1.0
|
||||
|
||||
prev_pos_x = data_key[X] + width
|
||||
prev_pos_y = data_key[Y]
|
||||
|
||||
if layout_row:
|
||||
layout_rows.append(layout_row)
|
||||
return layout_rows
|
||||
|
||||
|
||||
func _deserialize_key(data_key: Dictionary, pos_x: float, pos_y: float) -> Dictionary:
|
||||
var key_dict: Dictionary = {}
|
||||
|
||||
if data_key.has(W):
|
||||
key_dict[KeyProps.W] = data_key[W]
|
||||
if data_key.has(H):
|
||||
key_dict[KeyProps.H] = data_key[H]
|
||||
if pos_x != 0:
|
||||
key_dict[KeyProps.X] = pos_x
|
||||
if pos_y != 0:
|
||||
key_dict[KeyProps.Y] = pos_y
|
||||
|
||||
if data_key.has(R):
|
||||
key_dict[KeyProps.R] = data_key[R]
|
||||
if data_key.has(RX):
|
||||
key_dict[KeyProps.PX] = data_key[RX]
|
||||
if data_key.has(RY):
|
||||
key_dict[KeyProps.PY] = data_key[RY]
|
||||
|
||||
return key_dict
|
||||
|
||||
|
||||
func _get_keymap_keys(data_keys: Array, json_file_name: String) -> Error:
|
||||
var c_file_name := json_file_name.substr(0, json_file_name.rfind(".") + 1) + "c"
|
||||
|
||||
var file := FileAccess.open(
|
||||
LayoutConfig.CUSTOM_LAYOUTS_PATH.path_join(c_file_name), FileAccess.READ
|
||||
)
|
||||
if not file:
|
||||
var file_err := FileAccess.get_open_error()
|
||||
push_error(
|
||||
"%s: error opening file '%s': %s" % [_file_name, c_file_name, error_string(file_err)]
|
||||
)
|
||||
return FAILED
|
||||
|
||||
var content := file.get_as_text()
|
||||
var layout_regex := RegEx.new()
|
||||
layout_regex.compile(
|
||||
r".*=\s(?<name>LAYOUT.*)\s*\((?<keys>[^()]*(?:\([^()]*\)[^()]*)*)\)"
|
||||
)
|
||||
var keys_regex := RegEx.new()
|
||||
keys_regex.compile(r"\w+(?:\(.*?\))?")
|
||||
|
||||
var keymap_keys: Array[String] = []
|
||||
|
||||
var layout_match := layout_regex.search(content)
|
||||
if not layout_match:
|
||||
push_error(
|
||||
"%s: no layout keymap definitions found in '%s'" % [_file_name, c_file_name]
|
||||
)
|
||||
return FAILED
|
||||
|
||||
for key_matches in keys_regex.search_all(layout_match.get_string("keys")):
|
||||
keymap_keys.append(key_matches.get_string())
|
||||
|
||||
for i in range(mini(data_keys.size(), keymap_keys.size())):
|
||||
data_keys[i][KEYCODE] = keymap_keys[i]
|
||||
|
||||
return OK
|
||||
|
||||
|
||||
func _get_keymap_key_unprefixed(keymap_key_prefixed: String) -> String:
|
||||
return keymap_key_prefixed.substr(3)
|
||||
|
||||
|
||||
func _get_keycode_from_keymap_key(keymap_key_prefixed: String) -> Key:
|
||||
var keymap_key := _get_keymap_key_unprefixed(keymap_key_prefixed)
|
||||
var keycode := KEY_NONE
|
||||
|
||||
keycode = OS.find_keycode_from_string(keymap_key)
|
||||
|
||||
if keycode == KEY_NONE and KEYCODE_MAP.has(keymap_key):
|
||||
keycode = KEYCODE_MAP[keymap_key]
|
||||
|
||||
if keycode == KEY_NONE and KEYCODE_MODIFIER_LEFT_MAP.has(keymap_key):
|
||||
keycode = KEYCODE_MODIFIER_LEFT_MAP[keymap_key]
|
||||
|
||||
if keycode == KEY_NONE and KEYCODE_MODIFIER_RIGHT_MAP.has(keymap_key):
|
||||
keycode = KEYCODE_MODIFIER_RIGHT_MAP[keymap_key]
|
||||
|
||||
if keycode == KEY_NONE:
|
||||
push_warning(
|
||||
"%s: could not recognize key label '%s'" % [_file_name, keymap_key_prefixed]
|
||||
)
|
||||
_has_errors = true
|
||||
|
||||
return keycode
|
||||
|
||||
|
||||
func _get_key_location(keymap_key_prefixed: String) -> KeyLocation:
|
||||
var keymap_key := _get_keymap_key_unprefixed(keymap_key_prefixed)
|
||||
if KEYCODE_MODIFIER_LEFT_MAP.has(keymap_key):
|
||||
return KEY_LOCATION_LEFT
|
||||
if KEYCODE_MODIFIER_RIGHT_MAP.has(keymap_key):
|
||||
return KEY_LOCATION_RIGHT
|
||||
return KEY_LOCATION_UNSPECIFIED
|
||||
1
scripts/layouts/parser_qmk.gd.uid
Normal file
1
scripts/layouts/parser_qmk.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cieogkcrmre4q
|
||||
125
scripts/player.gd
Normal file
125
scripts/player.gd
Normal file
@ -0,0 +1,125 @@
|
||||
class_name Player extends Node3D
|
||||
|
||||
#region variables
|
||||
@export_group("References")
|
||||
@export var _keyboard: GameKeyboard
|
||||
@export var _sfx_player: AudioStreamPlayer3D
|
||||
|
||||
@export_group("Animation")
|
||||
|
||||
@export_subgroup("Moving")
|
||||
@export var _move_duration: float = 0.1
|
||||
@export var _move_hop_height: float = 1
|
||||
|
||||
@export_group("SFX")
|
||||
@export var _hop_sfx: AudioStream
|
||||
@export var _hop_sfx_volume: float = 0.4
|
||||
|
||||
var _current_key: GameKey
|
||||
var _starting_keycode: Key = KEY_H
|
||||
var _rotation: Quaternion
|
||||
|
||||
var _move_timer: float
|
||||
var _is_moving: bool
|
||||
var _prev_pos: Vector3
|
||||
|
||||
var _polyphonic: AudioStreamPlaybackPolyphonic
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region builtins
|
||||
func _ready() -> void:
|
||||
_keyboard.key_pressed.connect(_on_keyboard_key_pressed)
|
||||
_current_key = _keyboard.query_key_by_keycode(_starting_keycode)
|
||||
if _current_key:
|
||||
global_transform = _current_key.player_pos_marker.global_transform
|
||||
_keyboard.emit_player_current_key_changed(_current_key)
|
||||
|
||||
_sfx_player.play()
|
||||
_polyphonic = _sfx_player.get_stream_playback()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_animate(delta)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region movement
|
||||
func _move(game_key: GameKey) -> void:
|
||||
var look_from := _current_key.player_pos_marker.transform * _current_key.transform
|
||||
var look_target := game_key.player_pos_marker.transform * game_key.transform
|
||||
look_target.origin.y = look_from.origin.y
|
||||
var looking := look_from.looking_at(look_target.origin, Vector3.UP, true)
|
||||
_rotation = looking.basis.get_rotation_quaternion()
|
||||
|
||||
_prev_pos = _current_key.player_pos_marker.global_position
|
||||
_move_timer = _move_duration
|
||||
if _is_moving:
|
||||
_finish_move(true)
|
||||
_is_moving = true
|
||||
|
||||
_current_key = game_key
|
||||
_keyboard.emit_player_current_key_changed(game_key)
|
||||
_play_sfx(_hop_sfx, _hop_sfx_volume)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region animation
|
||||
func _animate(delta: float) -> void:
|
||||
if not _current_key:
|
||||
return
|
||||
|
||||
if _move_timer <= 0:
|
||||
if _is_moving:
|
||||
_is_moving = false
|
||||
_finish_move(false)
|
||||
global_position = _animate_position()
|
||||
else:
|
||||
_move_timer -= delta
|
||||
var lerp_value := 1 - (_move_timer / _move_duration)
|
||||
global_position = lerp(_prev_pos, _animate_position(), lerp_value)
|
||||
var hop_value := 1 - absf(lerp_value * 2 - 1)
|
||||
hop_value = 1 - pow(1 - hop_value, 3)
|
||||
global_position.y += hop_value * _move_hop_height
|
||||
|
||||
global_rotation = _animate_rotation()
|
||||
|
||||
|
||||
func _animate_position() -> Vector3:
|
||||
return _current_key.player_pos_marker.global_position
|
||||
|
||||
|
||||
func _animate_rotation() -> Vector3:
|
||||
var rot_translate := (
|
||||
_current_key.get_default_transform().inverse()
|
||||
* _current_key.player_pos_marker.global_transform
|
||||
)
|
||||
return (_rotation * rot_translate.basis.get_rotation_quaternion()).get_euler()
|
||||
|
||||
|
||||
func _finish_move(_interrupted: bool) -> void:
|
||||
_keyboard.emit_player_finished_move(_current_key)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region sounds
|
||||
func _play_sfx(stream: AudioStream, volume_linear: float = 1) -> void:
|
||||
_polyphonic.play_stream(stream, 0, linear_to_db(volume_linear))
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region event handlers
|
||||
func _on_keyboard_key_pressed(game_key: GameKey) -> void:
|
||||
if _current_key and _current_key.is_adjacent(game_key):
|
||||
_move(game_key)
|
||||
|
||||
#endregion
|
||||
1
scripts/player.gd.uid
Normal file
1
scripts/player.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dmfdlb4ae57qv
|
||||
@ -2,6 +2,8 @@
|
||||
# https://www.youtube.com/watch?v=KPoeNZZ6H4s
|
||||
class_name SecondOrderDynamics
|
||||
|
||||
var y: Vector3
|
||||
|
||||
var _k1: float
|
||||
var _k2: float
|
||||
var _k3: float
|
||||
@ -11,7 +13,6 @@ var _z: float
|
||||
var _d: float
|
||||
|
||||
var _xp: Vector3
|
||||
var _y: Vector3
|
||||
var _yd: Vector3
|
||||
|
||||
|
||||
@ -29,7 +30,7 @@ func _init(fzr: Vector3, x0: Vector3) -> void:
|
||||
_k3 = r * z / _w
|
||||
|
||||
_xp = x0
|
||||
_y = x0
|
||||
y = x0
|
||||
_yd = Vector3.ZERO
|
||||
|
||||
|
||||
@ -46,6 +47,7 @@ func process(t: float, x: Vector3, xd: Vector3 = Vector3.INF) -> Vector3:
|
||||
var beta := t1 * t1
|
||||
var t2 := t / (1 + beta - alpha)
|
||||
k2_stable = t * t2
|
||||
_y = _y + t * _yd
|
||||
_yd = _yd + t * (x + _k3 * xd - _y - _k1 * _yd) / k2_stable
|
||||
return _y
|
||||
y = y + t * _yd
|
||||
_yd = _yd + t * (x + _k3 * xd - y - _k1 * _yd) / k2_stable
|
||||
return y
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user