8 Commits

Author SHA1 Message Date
shawn fe9738ca46 Fix weapon switching, scale, add Q key bind
- Fixed weapon switching: player.gd now uses weapon_next/weapon_prev/slot actions
  (was checking for nonexistent 'weapon_toggle')
- Added Q key to weapon_next input action
- Reduced weapon scale from 0.3 to 0.07-0.08 (was still 3x too large)
2026-07-05 23:54:57 -04:00
shawn 0723b75f7b Fix weapon position, scale, switch to GLB imports
- Reduced weapon scale 10-13x (previously OBJ models at native size)
- Adjusted on-screen position for each weapon type
- Switched from OBJ to GLB imports (better pivot points)
- Fixed Revolver model coordinate issue
2026-07-05 23:29:30 -04:00
shawn 91b878f347 Quaternius weapons, input fixes, sound & path fixes
- Added 6 Quaternius weapon models (Pistol, Revolver, Rifle, Shotgun, P90, SniperRifle)
- Created weapon resource files with balanced stats
- Wired Pistol + Rifle into player.tscn replacing Kenney blasters
- Fixed WASD input (matched input map action names)
- Fixed Escape key toggles mouse capture
- Added Audio autoload singleton
- Fixed broken sound asset paths across all scripts
- Fixed all scene ext_resource text paths (nested under assets/kenney-fps/)
- Fixed all .import source_file paths
- Deleted stale .import files (will be regenerated by editor on open)
2026-07-05 22:53:27 -04:00
shawn 695e4db5cd Add input debug to window title for diagnostic
Shows Input.get_axis() values, weapon slot states, and fire
state in the window title bar — visible even when print()
output is lost on Windows GUI exports.
2026-07-03 23:13:42 -04:00
shawn c5b6b05801 Fix keyboard input: use OS.move_to_foreground() for window focus
get_window().grab_focus() was insufficient on Windows GUI exports.
Replaced with OS.move_to_foreground() + Input.set_mouse_mode(CAPTURED)
+ deferred retries to ensure the game window holds keyboard focus
after the connection UI is dismissed.
2026-07-03 22:34:50 -04:00
shawn ef4e4ccb80 Clean up test diagnostic files 2026-07-03 20:07:49 -04:00
shawn 7db8434f7e Fix duplicate _rollback_tick causing WeaponManager compile error
A debug-print attempt from earlier inadvertently appended a second
_rollback_tick function (with spaces instead of tabs) at the end of
weapon_manager.gd:419, causing the entire script to fail compilation.
This meant the WeaponManager node had NO script attached:
- 'has_method("set_default_loadout")' returned false
- Client-side loadout init never ran
- All weapon operations silently did nothing

This is the real root cause of why weapons never worked on the client.
2026-07-03 20:07:40 -04:00
shawn 7bbe4ba71d Fix weapons not working on client: remove is_predicting check + init loadout
- Removed the is_predicting() early-return in weapon_manager._rollback_tick
  which blocked all weapon actions on the client (player owned by server)
- Added client-side weapon loadout initialization in player-spawner._spawn
  so the player has weapons immediately, before server state sync arrives
- Server reconciles via rollback state sync

Movement was fixed in v0.2.2 (window focus grab). This addresses
the remaining 'can't switch or use weapons' issue.
2026-07-03 18:24:21 -04:00
157 changed files with 20404 additions and 989 deletions
@@ -1,36 +0,0 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://biqtga8moh7ah"
path="res://.godot/imported/lilita_one_regular.ttf-68cc4ab6825bdd499d1ad608190f5213.fontdata"
[deps]
source_file="res://fonts/lilita_one_regular.ttf"
dest_files=["res://.godot/imported/lilita_one_regular.ttf-68cc4ab6825bdd499d1ad608190f5213.fontdata"]
[params]
Rendering=null
antialiasing=1
generate_mipmaps=false
disable_embedded_bitmaps=true
multichannel_signed_distance_field=false
msdf_pixel_range=8
msdf_size=48
allow_system_fallback=true
force_autohinter=false
modulate_color_glyphs=false
hinting=1
subpixel_positioning=1
keep_rounding_remainders=true
oversampling=0.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
preload=[]
language_support={}
script_support={}
opentype_features={}
-40
View File
@@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cpxour8a7ihec"
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.png"
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
@@ -1,41 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b5hxnjwvrxuji"
path.bptc="res://.godot/imported/colormap.png-c1bc3c3aabeec406ff4b53328583776a.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://models/Textures/colormap.png"
dest_files=["res://.godot/imported/colormap.png-c1bc3c3aabeec406ff4b53328583776a.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
@@ -1,42 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://das5fosq6auoe"
path="res://.godot/imported/grass-small.glb-0be1c06405ef2ce478dc00acb5be5be8.scn"
[deps]
source_file="res://models/grass-small.glb"
dest_files=["res://.godot/imported/grass-small.glb-0be1c06405ef2ce478dc00acb5be5be8.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1
-42
View File
@@ -1,42 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://beaer7x07yif6"
path="res://.godot/imported/grass.glb-0ce858eae1c69c894a569863f13e24f1.scn"
[deps]
source_file="res://models/grass.glb"
dest_files=["res://.godot/imported/grass.glb-0ce858eae1c69c894a569863f13e24f1.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1
@@ -1,32 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://3403atnv0s14"
path="res://.godot/imported/platform-falling.glb-a1321a8710589d27b2a6e247cfe23359.scn"
[deps]
source_file="res://models/platform-falling.glb"
dest_files=["res://.godot/imported/platform-falling.glb-a1321a8710589d27b2a6e247cfe23359.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/apply_root_scale=true
nodes/root_scale=1.0
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/embedded_image_handling=1
@@ -1,42 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://k065rxqtim8f"
path="res://.godot/imported/platform-large-grass.glb-eba1099445789c9c9ba0b37fc57bd13f.scn"
[deps]
source_file="res://models/platform-large-grass.glb"
dest_files=["res://.godot/imported/platform-large-grass.glb-eba1099445789c9c9ba0b37fc57bd13f.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1
@@ -1,42 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bwgwd5ue8i2bt"
path="res://.godot/imported/platform.glb-3476c7cd272116fb58fd5d0a0eddb703.scn"
[deps]
source_file="res://models/platform.glb"
dest_files=["res://.godot/imported/platform.glb-3476c7cd272116fb58fd5d0a0eddb703.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1
@@ -1,42 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://c743irj356lax"
path="res://.godot/imported/wall-high.glb-74f2e81af43ef5254fe14637810326a5.scn"
[deps]
source_file="res://models/wall-high.glb"
dest_files=["res://.godot/imported/wall-high.glb-74f2e81af43ef5254fe14637810326a5.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1
@@ -1,42 +0,0 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://x08pt5dvke58"
path="res://.godot/imported/wall-low.glb-de080c69f0eef8b51181d363ed18f581.scn"
[deps]
source_file="res://models/wall-low.glb"
dest_files=["res://.godot/imported/wall-low.glb-de080c69f0eef8b51181d363ed18f581.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/embedded_image_handling=1
+2 -2
View File
@@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=3 uid="uid://oqfhfp1a80qd"]
[ext_resource type="PackedScene" uid="uid://g2w2608bc2dy" path="res://models/cloud.glb" id="1_2mo3p"]
[ext_resource type="Script" uid="uid://bfgpo8fvf136p" path="res://objects/cloud.gd" id="2_rmotl"]
[ext_resource type="PackedScene" uid="uid://g2w2608bc2dy" path="res://assets/kenney-fps/models/cloud.glb" id="1_2mo3p"]
[ext_resource type="Script" uid="uid://bfgpo8fvf136p" path="res://assets/kenney-fps/objects/cloud.gd" id="2_rmotl"]
[node name="cube2" instance=ExtResource("1_2mo3p")]
script = ExtResource("2_rmotl")
+3 -3
View File
@@ -28,7 +28,7 @@ func _process(delta):
# Take damage from player
func damage(amount):
Audio.play("sounds/enemy_hurt.ogg")
Audio.play("assets/kenney-fps/sounds/enemy_hurt.ogg")
health -= amount
@@ -38,7 +38,7 @@ func damage(amount):
# Destroy the enemy when out of health
func destroy():
Audio.play("sounds/enemy_destroy.ogg")
Audio.play("assets/kenney-fps/sounds/enemy_destroy.ogg")
destroyed = true
queue_free()
@@ -63,6 +63,6 @@ func _on_timer_timeout():
muzzle_b.play("default")
muzzle_b.rotation_degrees.z = randf_range(-45, 45)
Audio.play("sounds/enemy_attack.ogg")
Audio.play("assets/kenney-fps/sounds/enemy_attack.ogg")
collider.damage(5) # Apply damage to player
+3 -3
View File
@@ -1,8 +1,8 @@
[gd_scene load_steps=5 format=3 uid="uid://d2g78tpqbyf5g"]
[ext_resource type="PackedScene" uid="uid://lde2xq3vq635" path="res://models/enemy-flying.glb" id="1_3v8nl"]
[ext_resource type="Script" uid="uid://b6udw8uhlp4ei" path="res://objects/enemy.gd" id="1_jg24b"]
[ext_resource type="SpriteFrames" uid="uid://dbv3sy5qjatnl" path="res://sprites/burst_animation.tres" id="3_iblw5"]
[ext_resource type="PackedScene" uid="uid://lde2xq3vq635" path="res://assets/kenney-fps/models/enemy-flying.glb" id="1_3v8nl"]
[ext_resource type="Script" uid="uid://b6udw8uhlp4ei" path="res://assets/kenney-fps/objects/enemy.gd" id="1_jg24b"]
[ext_resource type="SpriteFrames" uid="uid://dbv3sy5qjatnl" path="res://assets/kenney-fps/sprites/burst_animation.tres" id="3_iblw5"]
[sub_resource type="SphereShape3D" id="SphereShape3D_iix87"]
radius = 0.75
+2 -2
View File
@@ -1,7 +1,7 @@
[gd_scene load_steps=8 format=3 uid="uid://b7070gfoko4mo"]
[ext_resource type="Texture2D" uid="uid://dh0t42ubhuv0" path="res://sprites/hit.png" id="1_mdfft"]
[ext_resource type="Script" uid="uid://bwkexrmt718dx" path="res://objects/impact.gd" id="2_k826h"]
[ext_resource type="Texture2D" uid="uid://dh0t42ubhuv0" path="res://assets/kenney-fps/sprites/hit.png" id="1_mdfft"]
[ext_resource type="Script" uid="uid://bwkexrmt718dx" path="res://assets/kenney-fps/objects/impact.gd" id="2_k826h"]
[sub_resource type="AtlasTexture" id="AtlasTexture_8c04i"]
atlas = ExtResource("1_mdfft")
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+27 -17
View File
@@ -84,7 +84,7 @@ func _process(delta):
camera.position.y = lerp(camera.position.y, 0.0, delta * 5)
if is_on_floor() and gravity > 1 and !previously_floored: # Landed
Audio.play("sounds/land.ogg")
Audio.play("assets/kenney-fps/sounds/land.ogg")
camera.position.y = -0.1
previously_floored = is_on_floor()
@@ -102,19 +102,18 @@ func _input(event):
handle_rotation(event.relative.x, event.relative.y, false)
func handle_controls(delta):
# Mouse capture
if Input.is_action_just_pressed("mouse_capture"):
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
mouse_captured = true
if Input.is_action_just_pressed("mouse_capture_exit"):
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
mouse_captured = false
input_mouse = Vector2.ZERO
# Mouse capture — Escape toggles
if Input.is_action_just_pressed("escape"):
if mouse_captured:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
mouse_captured = false
input_mouse = Vector2.ZERO
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
mouse_captured = true
# Movement
var input := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
var input := Input.get_vector("move_west", "move_east", "move_north", "move_south")
movement_velocity = Vector3(input.x, 0, input.y).normalized() * movement_speed
# Handle Controller Rotation
@@ -165,7 +164,7 @@ func handle_gravity(delta):
# Jumping
func action_jump():
Audio.play("sounds/jump_a.ogg, sounds/jump_b.ogg, sounds/jump_c.ogg")
Audio.play("assets/kenney-fps/sounds/jump_a.ogg, assets/kenney-fps/sounds/jump_b.ogg, assets/kenney-fps/sounds/jump_c.ogg")
gravity = - jump_strength
jumps_remaining -= 1
@@ -206,7 +205,7 @@ func action_shoot():
# Creating an impact animation
var impact = preload("res://objects/impact.tscn")
var impact = preload("res://assets/kenney-fps/objects/impact.tscn")
var impact_instance = impact.instantiate()
impact_instance.play("shot")
@@ -228,11 +227,22 @@ func action_shoot():
# Toggle between available weapons (listed in 'weapons')
func action_weapon_toggle():
if Input.is_action_just_pressed("weapon_toggle"):
if Input.is_action_just_pressed("weapon_next"):
weapon_index = wrap(weapon_index + 1, 0, weapons.size())
initiate_change_weapon(weapon_index)
Audio.play("sounds/weapon_change.ogg")
Audio.play("assets/kenney-fps/sounds/weapon_change.ogg")
if Input.is_action_just_pressed("weapon_prev"):
weapon_index = wrap(weapon_index - 1, 0, weapons.size())
initiate_change_weapon(weapon_index)
Audio.play("assets/kenney-fps/sounds/weapon_change.ogg")
for i in range(4):
if Input.is_action_just_pressed("weapon_slot_" + str(i + 1)):
if i < weapons.size():
weapon_index = i
initiate_change_weapon(weapon_index)
Audio.play("assets/kenney-fps/sounds/weapon_change.ogg")
# Initiates the weapon changing animation (tween)
+7 -7
View File
@@ -1,12 +1,12 @@
[gd_scene load_steps=9 format=3 uid="uid://dl2ed4gkybggf"]
[ext_resource type="Script" uid="uid://cu47grjp072jk" path="res://objects/player.gd" id="1_ffboj"]
[ext_resource type="Resource" uid="uid://cu2gtxlcmbb34" path="res://weapons/blaster-repeater.tres" id="2_6epbw"]
[ext_resource type="Texture2D" uid="uid://8ggihh27mlrr" path="res://sprites/blob_shadow.png" id="2_b0fo8"]
[ext_resource type="Script" uid="uid://dg01pkkc1c5vd" path="res://scripts/weapon.gd" id="2_i825w"]
[ext_resource type="Resource" uid="uid://c56y8pqoyk15f" path="res://weapons/blaster.tres" id="3_kr4p8"]
[ext_resource type="SpriteFrames" uid="uid://dbv3sy5qjatnl" path="res://sprites/burst_animation.tres" id="4_m6ukc"]
[ext_resource type="AudioStream" uid="uid://cydjn1ct3hps2" path="res://sounds/walking.ogg" id="5_ics1s"]
[ext_resource type="Script" uid="uid://cu47grjp072jk" path="res://assets/kenney-fps/objects/player.gd" id="1_ffboj"]
[ext_resource type="Resource" uid="uid://quaternius_pistol_auto" path="res://assets/quaternius/weapons/pistol.tres" id="2_6epbw"]
[ext_resource type="Texture2D" uid="uid://8ggihh27mlrr" path="res://assets/kenney-fps/sprites/blob_shadow.png" id="2_b0fo8"]
[ext_resource type="Script" uid="uid://dg01pkkc1c5vd" path="res://assets/kenney-fps/scripts/weapon.gd" id="2_i825w"]
[ext_resource type="Resource" uid="uid://quaternius_rifle_auto" path="res://assets/quaternius/weapons/rifle.tres" id="3_kr4p8"]
[ext_resource type="SpriteFrames" uid="uid://dbv3sy5qjatnl" path="res://assets/kenney-fps/sprites/burst_animation.tres" id="4_m6ukc"]
[ext_resource type="AudioStream" uid="uid://cydjn1ct3hps2" path="res://assets/kenney-fps/sounds/walking.ogg" id="5_ics1s"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_gdq8c"]
radius = 0.3
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10 -10
View File
@@ -1,15 +1,15 @@
[gd_scene format=3 uid="uid://dxvvlck8lej3f"]
[ext_resource type="Environment" uid="uid://jvmpkdwaeaq" path="res://scenes/main-environment.tres" id="1_q8fpv"]
[ext_resource type="PackedScene" uid="uid://dl2ed4gkybggf" path="res://objects/player.tscn" id="2_elriq"]
[ext_resource type="Script" uid="uid://by0qn28x1i1jj" path="res://scripts/hud.gd" id="3_s8mkj"]
[ext_resource type="FontFile" uid="uid://biqtga8moh7ah" path="res://fonts/lilita_one_regular.ttf" id="3_w27de"]
[ext_resource type="PackedScene" uid="uid://dpm3l05d7fu35" path="res://objects/platform.tscn" id="5_3s40e"]
[ext_resource type="PackedScene" uid="uid://r7rt7pth4u7o" path="res://objects/wall_low.tscn" id="5_6vel1"]
[ext_resource type="PackedScene" uid="uid://c71evdjblk5wp" path="res://objects/wall_high.tscn" id="7_cabne"]
[ext_resource type="PackedScene" uid="uid://bvx5cvigosg0s" path="res://objects/platform_large_grass.tscn" id="7_wggef"]
[ext_resource type="PackedScene" uid="uid://d2g78tpqbyf5g" path="res://objects/enemy.tscn" id="8_7ty2f"]
[ext_resource type="PackedScene" uid="uid://oqfhfp1a80qd" path="res://objects/cloud.tscn" id="10_5ksau"]
[ext_resource type="Environment" uid="uid://jvmpkdwaeaq" path="res://assets/kenney-fps/scenes/main-environment.tres" id="1_q8fpv"]
[ext_resource type="PackedScene" uid="uid://dl2ed4gkybggf" path="res://assets/kenney-fps/objects/player.tscn" id="2_elriq"]
[ext_resource type="Script" uid="uid://by0qn28x1i1jj" path="res://assets/kenney-fps/scripts/hud.gd" id="3_s8mkj"]
[ext_resource type="FontFile" uid="uid://biqtga8moh7ah" path="res://assets/kenney-fps/fonts/lilita_one_regular.ttf" id="3_w27de"]
[ext_resource type="PackedScene" uid="uid://dpm3l05d7fu35" path="res://assets/kenney-fps/objects/platform.tscn" id="5_3s40e"]
[ext_resource type="PackedScene" uid="uid://r7rt7pth4u7o" path="res://assets/kenney-fps/objects/wall_low.tscn" id="5_6vel1"]
[ext_resource type="PackedScene" uid="uid://c71evdjblk5wp" path="res://assets/kenney-fps/objects/wall_high.tscn" id="7_cabne"]
[ext_resource type="PackedScene" uid="uid://bvx5cvigosg0s" path="res://assets/kenney-fps/objects/platform_large_grass.tscn" id="7_wggef"]
[ext_resource type="PackedScene" uid="uid://d2g78tpqbyf5g" path="res://assets/kenney-fps/objects/enemy.tscn" id="8_7ty2f"]
[ext_resource type="PackedScene" uid="uid://oqfhfp1a80qd" path="res://assets/kenney-fps/objects/cloud.tscn" id="10_5ksau"]
[sub_resource type="LabelSettings" id="LabelSettings_fpmwk"]
font = ExtResource("3_w27de")
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dkftljsbdhwng"
path="res://.godot/imported/blaster.ogg-1527ec84d518b60bb9fbaad97656d870.oggvorbisstr"
[deps]
source_file="res://sounds/blaster.ogg"
dest_files=["res://.godot/imported/blaster.ogg-1527ec84d518b60bb9fbaad97656d870.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://csdsvg0nh35id"
path="res://.godot/imported/blaster_repeater.ogg-a46ceca73f68a6e3e41e971b07cb8de8.oggvorbisstr"
[deps]
source_file="res://sounds/blaster_repeater.ogg"
dest_files=["res://.godot/imported/blaster_repeater.ogg-a46ceca73f68a6e3e41e971b07cb8de8.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://blxqudiclpq84"
path="res://.godot/imported/enemy_attack.ogg-ad3c73e6e91698e3fc81bd6423d8fca2.oggvorbisstr"
[deps]
source_file="res://sounds/enemy_attack.ogg"
dest_files=["res://.godot/imported/enemy_attack.ogg-ad3c73e6e91698e3fc81bd6423d8fca2.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://br1ov3ld0er6r"
path="res://.godot/imported/enemy_destroy.ogg-feb8f111abab93d5fc30ec838c975232.oggvorbisstr"
[deps]
source_file="res://sounds/enemy_destroy.ogg"
dest_files=["res://.godot/imported/enemy_destroy.ogg-feb8f111abab93d5fc30ec838c975232.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cgnbyrnxj12ed"
path="res://.godot/imported/enemy_hurt.ogg-64e07bfa21b7ae57b8a0bf7357a95008.oggvorbisstr"
[deps]
source_file="res://sounds/enemy_hurt.ogg"
dest_files=["res://.godot/imported/enemy_hurt.ogg-64e07bfa21b7ae57b8a0bf7357a95008.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bvnum0u73y8sv"
path="res://.godot/imported/jump_a.ogg-df3a87b422b8537163b811137ab76d0c.oggvorbisstr"
[deps]
source_file="res://sounds/jump_a.ogg"
dest_files=["res://.godot/imported/jump_a.ogg-df3a87b422b8537163b811137ab76d0c.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://5raj3p7xaecq"
path="res://.godot/imported/jump_b.ogg-f0989d7351debef348a67b21be8b7bd0.oggvorbisstr"
[deps]
source_file="res://sounds/jump_b.ogg"
dest_files=["res://.godot/imported/jump_b.ogg-f0989d7351debef348a67b21be8b7bd0.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://7tnsakv8pwa5"
path="res://.godot/imported/jump_c.ogg-6a4808f519ad4af845e78fe92b3da78c.oggvorbisstr"
[deps]
source_file="res://sounds/jump_c.ogg"
dest_files=["res://.godot/imported/jump_c.ogg-6a4808f519ad4af845e78fe92b3da78c.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
-19
View File
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://xnxidwkp46un"
path="res://.godot/imported/land.ogg-7222ba872273a4a7535937ef5cfdffd0.oggvorbisstr"
[deps]
source_file="res://sounds/land.ogg"
dest_files=["res://.godot/imported/land.ogg-7222ba872273a4a7535937ef5cfdffd0.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cydjn1ct3hps2"
path="res://.godot/imported/walking.ogg-bf61e9916135189ff0d5c06a148b02ab.oggvorbisstr"
[deps]
source_file="res://sounds/walking.ogg"
dest_files=["res://.godot/imported/walking.ogg-bf61e9916135189ff0d5c06a148b02ab.oggvorbisstr"]
[params]
loop=true
loop_offset=0.0
bpm=0.0
beat_count=0
bar_beats=4
@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cmc2ufr6o30gl"
path="res://.godot/imported/weapon_change.ogg-7ebfd5dcd3026b79e24db351d30b1a1b.oggvorbisstr"
[deps]
source_file="res://sounds/weapon_change.ogg"
dest_files=["res://.godot/imported/weapon_change.ogg-7ebfd5dcd3026b79e24db351d30b1a1b.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4
@@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://biff4dkpfqi47"
path="res://.godot/imported/splash-screen.png-ae6c8b07e185ee8a074576008d9ccc5a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://splash-screen.png"
dest_files=["res://.godot/imported/splash-screen.png-ae6c8b07e185ee8a074576008d9ccc5a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
@@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://8ggihh27mlrr"
path="res://.godot/imported/blob_shadow.png-d19f4ffceb1d99dd3331acec2dc6d7df.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://sprites/blob_shadow.png"
dest_files=["res://.godot/imported/blob_shadow.png-d19f4ffceb1d99dd3331acec2dc6d7df.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
@@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cbfhj0tswcksm"
path="res://.godot/imported/burst.png-0ca3a873296b113f4396e0bdead4beff.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://sprites/burst.png"
dest_files=["res://.godot/imported/burst.png-0ca3a873296b113f4396e0bdead4beff.ctex"]
[params]
compress/mode=1
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=false
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
@@ -3,38 +3,29 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://ce3lgq7foiusl"
path="res://.godot/imported/crosshair-repeater.png-ec408225e689b23875963003728cb016.ctex"
metadata={
"vram_texture": false
}
path="res://.godot/imported/crosshair-repeater.png-d589090035088acabaed3ef868d992be.ctex"
[deps]
source_file="res://sprites/crosshair-repeater.png"
dest_files=["res://.godot/imported/crosshair-repeater.png-ec408225e689b23875963003728cb016.ctex"]
source_file="res://assets/kenney-fps/sprites/crosshair-repeater.png"
dest_files=["res://.godot/imported/crosshair-repeater.png-d589090035088acabaed3ef868d992be.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
filter/fix_alpha_border=true
filter/premult=true
filter/repeat=true
filter/filter_clip=false
filter/anisotropic=0
detect_3d/compress_to=0
svg/scale=1.0
+10 -20
View File
@@ -3,24 +3,18 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://2jld33y6h5pq"
path.s3tc="res://.godot/imported/crosshair.png-1281ee32d6777347e0f43a202c603c1b.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
path="res://.godot/imported/crosshair.png-1a2174ba945cb2724603c288af43e347.ctex"
[deps]
source_file="res://sprites/crosshair.png"
dest_files=["res://.godot/imported/crosshair.png-1281ee32d6777347e0f43a202c603c1b.s3tc.ctex"]
source_file="res://assets/kenney-fps/sprites/crosshair.png"
dest_files=["res://.godot/imported/crosshair.png-1a2174ba945cb2724603c288af43e347.ctex"]
[params]
compress/mode=2
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
@@ -28,14 +22,10 @@ mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
filter/fix_alpha_border=true
filter/premult=true
filter/repeat=true
filter/filter_clip=false
filter/anisotropic=0
detect_3d/compress_to=0
svg/scale=1.0
-41
View File
@@ -1,41 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dh0t42ubhuv0"
path.s3tc="res://.godot/imported/hit.png-32857f07c817e04581303953f2545db0.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://sprites/hit.png"
dest_files=["res://.godot/imported/hit.png-32857f07c817e04581303953f2545db0.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
@@ -1,41 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cb7sdk1i5rx04"
path.bptc="res://.godot/imported/skybox.png-3a75a15c5dbffccdaa926738885bd0eb.bptc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://sprites/skybox.png"
dest_files=["res://.godot/imported/skybox.png-3a75a15c5dbffccdaa926738885bd0eb.bptc.ctex"]
[params]
compress/mode=2
compress/high_quality=true
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=false
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
@@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=4 format=3 uid="uid://cu2gtxlcmbb34"]
[ext_resource type="Texture2D" uid="uid://ce3lgq7foiusl" path="res://sprites/crosshair-repeater.png" id="1_hoqei"]
[ext_resource type="Script" uid="uid://dg01pkkc1c5vd" path="res://scripts/weapon.gd" id="1_l1atd"]
[ext_resource type="PackedScene" uid="uid://dd3oln1ucgqpd" path="res://models/blaster-repeater.glb" id="2_h64nv"]
[ext_resource type="Texture2D" uid="uid://ce3lgq7foiusl" path="res://assets/kenney-fps/sprites/crosshair-repeater.png" id="1_hoqei"]
[ext_resource type="Script" uid="uid://dg01pkkc1c5vd" path="res://assets/kenney-fps/scripts/weapon.gd" id="1_l1atd"]
[ext_resource type="PackedScene" uid="uid://dd3oln1ucgqpd" path="res://assets/kenney-fps/models/blaster-repeater.glb" id="2_h64nv"]
[resource]
script = ExtResource("1_l1atd")
@@ -12,5 +12,5 @@ muzzle_position = Vector3(0.1, -0.4, 1.5)
damage = 10.0
spread = 0.5
knockback = 10
sound_shoot = "sounds/blaster_repeater.ogg"
sound_shoot = "assets/kenney-fps/sounds/blaster_repeater.ogg"
crosshair = ExtResource("1_hoqei")
+4 -4
View File
@@ -1,8 +1,8 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=4 format=3 uid="uid://c56y8pqoyk15f"]
[ext_resource type="Texture2D" uid="uid://2jld33y6h5pq" path="res://sprites/crosshair.png" id="1_2onsr"]
[ext_resource type="PackedScene" uid="uid://b2p7bbkuxf7m" path="res://models/blaster.glb" id="1_x0glg"]
[ext_resource type="Script" uid="uid://dg01pkkc1c5vd" path="res://scripts/weapon.gd" id="2_107w7"]
[ext_resource type="Texture2D" uid="uid://2jld33y6h5pq" path="res://assets/kenney-fps/sprites/crosshair.png" id="1_2onsr"]
[ext_resource type="PackedScene" uid="uid://b2p7bbkuxf7m" path="res://assets/kenney-fps/models/blaster.glb" id="1_x0glg"]
[ext_resource type="Script" uid="uid://dg01pkkc1c5vd" path="res://assets/kenney-fps/scripts/weapon.gd" id="2_107w7"]
[resource]
script = ExtResource("2_107w7")
@@ -15,5 +15,5 @@ shot_count = 3
knockback = 40
min_knockback = Vector2(0.025, 0.025)
max_knockback = Vector2(0.045, 0.04)
sound_shoot = "sounds/blaster.ogg"
sound_shoot = "assets/kenney-fps/sounds/blaster.ogg"
crosshair = ExtResource("1_2onsr")
Binary file not shown.
@@ -3,19 +3,21 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://g2w2608bc2dy"
path="res://.godot/imported/cloud.glb-53daedc24f8d7f4baa42baf232c66643.scn"
uid="uid://bax08y0t8hkc"
path="res://.godot/imported/P90.glb-c0686bb17e63ef864fe47e2ef85c3e8d.scn"
[deps]
source_file="res://models/cloud.glb"
dest_files=["res://.godot/imported/cloud.glb-53daedc24f8d7f4baa42baf232c66643.scn"]
source_file="res://assets/quaternius/models/P90.glb"
dest_files=["res://.godot/imported/P90.glb-c0686bb17e63ef864fe47e2ef85c3e8d.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@@ -38,5 +40,6 @@ materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=1
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
+32
View File
@@ -0,0 +1,32 @@
# Blender MTL File: 'P90.blend'
# Material Count: 3
newmtl Material.001
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Material.003
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Material.004
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://dib3ephrwejf1"
path="res://.godot/imported/P90.obj-8df31986961b4a8e1ac0386a3807b1da.mesh"
[deps]
files=["res://.godot/imported/P90.obj-8df31986961b4a8e1ac0386a3807b1da.mesh"]
source_file="res://assets/quaternius/models/P90.obj"
dest_files=["res://.godot/imported/P90.obj-8df31986961b4a8e1ac0386a3807b1da.mesh", "res://.godot/imported/P90.obj-8df31986961b4a8e1ac0386a3807b1da.mesh"]
[params]
generate_tangents=true
generate_lods=true
generate_shadow_mesh=true
generate_lightmap_uv2=false
generate_lightmap_uv2_texel_size=0.2
scale_mesh=Vector3(1, 1, 1)
offset_mesh=Vector3(0, 0, 0)
force_disable_mesh_compression=false
Binary file not shown.
@@ -3,19 +3,21 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://lde2xq3vq635"
path="res://.godot/imported/enemy-flying.glb-6120572f403485a0abe68ce33d56638b.scn"
uid="uid://dsitqopjjyv0f"
path="res://.godot/imported/Pistol.glb-89dbea87b37e6ee1eb7a927c1a2eeafb.scn"
[deps]
source_file="res://models/enemy-flying.glb"
dest_files=["res://.godot/imported/enemy-flying.glb-6120572f403485a0abe68ce33d56638b.scn"]
source_file="res://assets/quaternius/models/Pistol.glb"
dest_files=["res://.godot/imported/Pistol.glb-89dbea87b37e6ee1eb7a927c1a2eeafb.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@@ -38,5 +40,6 @@ materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
+62
View File
@@ -0,0 +1,62 @@
# Blender MTL File: 'Pistol.blend'
# Material Count: 6
newmtl DarkerMetal
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Magazine
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Material.003
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Metal
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Muzzle
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Wood
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://cpt164o0tk18a"
path="res://.godot/imported/Pistol.obj-14b0d39d64d4f9501f6e7999f30e6929.mesh"
[deps]
files=["res://.godot/imported/Pistol.obj-14b0d39d64d4f9501f6e7999f30e6929.mesh"]
source_file="res://assets/quaternius/models/Pistol.obj"
dest_files=["res://.godot/imported/Pistol.obj-14b0d39d64d4f9501f6e7999f30e6929.mesh", "res://.godot/imported/Pistol.obj-14b0d39d64d4f9501f6e7999f30e6929.mesh"]
[params]
generate_tangents=true
generate_lods=true
generate_shadow_mesh=true
generate_lightmap_uv2=false
generate_lightmap_uv2_texel_size=0.2
scale_mesh=Vector3(1, 1, 1)
offset_mesh=Vector3(0, 0, 0)
force_disable_mesh_compression=false
Binary file not shown.
@@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dsie0nqm2cynl"
path="res://.godot/imported/Revolver.glb-b3a8e05b70649269c157ffc7301e7705.scn"
[deps]
source_file="res://assets/quaternius/models/Revolver.glb"
dest_files=["res://.godot/imported/Revolver.glb-b3a8e05b70649269c157ffc7301e7705.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
+52
View File
@@ -0,0 +1,52 @@
# Blender MTL File: 'Revolver.blend'
# Material Count: 5
newmtl Black
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl BulletTip
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl BulletYellow
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Metal
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Wood
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://daivcihpvdn5q"
path="res://.godot/imported/Revolver.obj-d2a80e1f299a317971f481f2a64d6638.mesh"
[deps]
files=["res://.godot/imported/Revolver.obj-d2a80e1f299a317971f481f2a64d6638.mesh"]
source_file="res://assets/quaternius/models/Revolver.obj"
dest_files=["res://.godot/imported/Revolver.obj-d2a80e1f299a317971f481f2a64d6638.mesh", "res://.godot/imported/Revolver.obj-d2a80e1f299a317971f481f2a64d6638.mesh"]
[params]
generate_tangents=true
generate_lods=true
generate_shadow_mesh=true
generate_lightmap_uv2=false
generate_lightmap_uv2_texel_size=0.2
scale_mesh=Vector3(1, 1, 1)
offset_mesh=Vector3(0, 0, 0)
force_disable_mesh_compression=false
Binary file not shown.
@@ -3,19 +3,21 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://b2p7bbkuxf7m"
path="res://.godot/imported/blaster.glb-865d95bc6d15a20a70b45829eaeb4dc5.scn"
uid="uid://dq6hh5r08lg30"
path="res://.godot/imported/Rifle.glb-8f0d9d0b7acc6e4bd68aa2695c1cdf0b.scn"
[deps]
source_file="res://models/blaster.glb"
dest_files=["res://.godot/imported/blaster.glb-865d95bc6d15a20a70b45829eaeb4dc5.scn"]
source_file="res://assets/quaternius/models/Rifle.glb"
dest_files=["res://.godot/imported/Rifle.glb-8f0d9d0b7acc6e4bd68aa2695c1cdf0b.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@@ -38,5 +40,6 @@ materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
+72
View File
@@ -0,0 +1,72 @@
# Blender MTL File: 'Rifle.blend'
# Material Count: 7
newmtl Barrels
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Black
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl BulletOrange
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl BulletYellow
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl DarkWood
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl LightWood
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Metal
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://cjej2m67fp777"
path="res://.godot/imported/Rifle.obj-e07611af2416a9a8a736e6c4a5299fc0.mesh"
[deps]
files=["res://.godot/imported/Rifle.obj-e07611af2416a9a8a736e6c4a5299fc0.mesh"]
source_file="res://assets/quaternius/models/Rifle.obj"
dest_files=["res://.godot/imported/Rifle.obj-e07611af2416a9a8a736e6c4a5299fc0.mesh", "res://.godot/imported/Rifle.obj-e07611af2416a9a8a736e6c4a5299fc0.mesh"]
[params]
generate_tangents=true
generate_lods=true
generate_shadow_mesh=true
generate_lightmap_uv2=false
generate_lightmap_uv2_texel_size=0.2
scale_mesh=Vector3(1, 1, 1)
offset_mesh=Vector3(0, 0, 0)
force_disable_mesh_compression=false
Binary file not shown.
@@ -3,19 +3,21 @@
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dd3oln1ucgqpd"
path="res://.godot/imported/blaster-repeater.glb-5f1125651aecb8bf54890e793b8c6da4.scn"
uid="uid://bfofgtyblb50p"
path="res://.godot/imported/Shotgun.glb-a190f57e8908ce676f957c7ad67f3129.scn"
[deps]
source_file="res://models/blaster-repeater.glb"
dest_files=["res://.godot/imported/blaster-repeater.glb-5f1125651aecb8bf54890e793b8c6da4.scn"]
source_file="res://assets/quaternius/models/Shotgun.glb"
dest_files=["res://.godot/imported/Shotgun.glb-a190f57e8908ce676f957c7ad67f3129.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Scene Root"
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
@@ -38,5 +40,6 @@ materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=0
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
+62
View File
@@ -0,0 +1,62 @@
# Blender MTL File: 'Shotgun.blend'
# Material Count: 6
newmtl Barrels
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Black
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl BulletRed
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl BulletYellow
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl DarkMetal
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Trigger
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://cf5at01ffsvs8"
path="res://.godot/imported/Shotgun.obj-9a9f670f1bcd4077708d4393434307ac.mesh"
[deps]
files=["res://.godot/imported/Shotgun.obj-9a9f670f1bcd4077708d4393434307ac.mesh"]
source_file="res://assets/quaternius/models/Shotgun.obj"
dest_files=["res://.godot/imported/Shotgun.obj-9a9f670f1bcd4077708d4393434307ac.mesh", "res://.godot/imported/Shotgun.obj-9a9f670f1bcd4077708d4393434307ac.mesh"]
[params]
generate_tangents=true
generate_lods=true
generate_shadow_mesh=true
generate_lightmap_uv2=false
generate_lightmap_uv2_texel_size=0.2
scale_mesh=Vector3(1, 1, 1)
offset_mesh=Vector3(0, 0, 0)
force_disable_mesh_compression=false
Binary file not shown.
@@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://d184porf2jvc8"
path="res://.godot/imported/SniperRifle.glb-6dc460dffa4334d5e53eb36b77857af1.scn"
[deps]
source_file="res://assets/quaternius/models/SniperRifle.glb"
dest_files=["res://.godot/imported/SniperRifle.glb-6dc460dffa4334d5e53eb36b77857af1.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
+82
View File
@@ -0,0 +1,82 @@
# Blender MTL File: 'SniperRifle.blend'
# Material Count: 8
newmtl Barrel
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Black
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl BulletOrange
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl BulletYellow
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Green
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Magazine
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Metal
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Trigger
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://ce5q4u4m722kv"
path="res://.godot/imported/SniperRifle.obj-41dcedbb93cd893f0676f56a9c467849.mesh"
[deps]
files=["res://.godot/imported/SniperRifle.obj-41dcedbb93cd893f0676f56a9c467849.mesh"]
source_file="res://assets/quaternius/models/SniperRifle.obj"
dest_files=["res://.godot/imported/SniperRifle.obj-41dcedbb93cd893f0676f56a9c467849.mesh", "res://.godot/imported/SniperRifle.obj-41dcedbb93cd893f0676f56a9c467849.mesh"]
[params]
generate_tangents=true
generate_lods=true
generate_shadow_mesh=true
generate_lightmap_uv2=false
generate_lightmap_uv2_texel_size=0.2
scale_mesh=Vector3(1, 1, 1)
offset_mesh=Vector3(0, 0, 0)
force_disable_mesh_compression=false
+22
View File
@@ -0,0 +1,22 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=4 format=3]
[ext_resource type="Script" path="res://assets/kenney-fps/scripts/weapon.gd" id="1"]
[ext_resource type="PackedScene" path="res://assets/quaternius/weapons/p90.tscn" id="2"]
[ext_resource type="Texture2D" uid="uid://ce3lgq7foiusl" path="res://assets/kenney-fps/sprites/crosshair-repeater.png" id="3"]
[resource]
script = ExtResource("1")
model = ExtResource("2")
position = Vector3(0.35, -0.35, -0.4)
crosshair = ExtResource("3")
rotation = Vector3(0, 180, 0)
muzzle_position = Vector3(0.05, -0.3, 0.8)
cooldown = 0.08
max_distance = 15
damage = 18
spread = 1.0
shot_count = 1
knockback = 10
min_knockback = Vector2(0.025, 0.025)
max_knockback = Vector2(0.045, 0.04)
sound_shoot = "assets/kenney-fps/sounds/blaster_repeater.ogg"
+8
View File
@@ -0,0 +1,8 @@
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://assets/quaternius/models/P90.glb" id="1"]
[node name="P90" type="Node3D"]
scale = Vector3(0.07, 0.07, 0.07)
[node name="Model" parent="." instance=ExtResource("1")]
+22
View File
@@ -0,0 +1,22 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=4 format=3]
[ext_resource type="Script" path="res://assets/kenney-fps/scripts/weapon.gd" id="1"]
[ext_resource type="PackedScene" path="res://assets/quaternius/weapons/pistol.tscn" id="2"]
[ext_resource type="Texture2D" uid="uid://2jld33y6h5pq" path="res://assets/kenney-fps/sprites/crosshair.png" id="3"]
[resource]
script = ExtResource("1")
model = ExtResource("2")
position = Vector3(0.45, -0.35, -0.5)
crosshair = ExtResource("3")
rotation = Vector3(0, 180, 0)
muzzle_position = Vector3(0.08, -0.35, 1.2)
cooldown = 0.3
max_distance = 15
damage = 25
spread = 0.5
shot_count = 1
knockback = 15
min_knockback = Vector2(0.025, 0.025)
max_knockback = Vector2(0.045, 0.04)
sound_shoot = "assets/kenney-fps/sounds/blaster.ogg"
+8
View File
@@ -0,0 +1,8 @@
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://assets/quaternius/models/Pistol.glb" id="1"]
[node name="Pistol" type="Node3D"]
scale = Vector3(0.08, 0.08, 0.08)
[node name="Model" parent="." instance=ExtResource("1")]
+22
View File
@@ -0,0 +1,22 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=4 format=3]
[ext_resource type="Script" path="res://assets/kenney-fps/scripts/weapon.gd" id="1"]
[ext_resource type="PackedScene" path="res://assets/quaternius/weapons/revolver.tscn" id="2"]
[ext_resource type="Texture2D" uid="uid://2jld33y6h5pq" path="res://assets/kenney-fps/sprites/crosshair.png" id="3"]
[resource]
script = ExtResource("1")
model = ExtResource("2")
position = Vector3(0.5, -0.35, -0.5)
crosshair = ExtResource("3")
rotation = Vector3(0, 180, 0)
muzzle_position = Vector3(0.1, -0.35, 1.2)
cooldown = 0.6
max_distance = 15
damage = 50
spread = 0.3
shot_count = 1
knockback = 25
min_knockback = Vector2(0.025, 0.025)
max_knockback = Vector2(0.045, 0.04)
sound_shoot = "assets/kenney-fps/sounds/blaster.ogg"
+8
View File
@@ -0,0 +1,8 @@
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://assets/quaternius/models/Revolver.glb" id="1"]
[node name="Revolver" type="Node3D"]
scale = Vector3(0.08, 0.08, 0.08)
[node name="Model" parent="." instance=ExtResource("1")]
+22
View File
@@ -0,0 +1,22 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=4 format=3]
[ext_resource type="Script" path="res://assets/kenney-fps/scripts/weapon.gd" id="1"]
[ext_resource type="PackedScene" path="res://assets/quaternius/weapons/rifle.tscn" id="2"]
[ext_resource type="Texture2D" uid="uid://2jld33y6h5pq" path="res://assets/kenney-fps/sprites/crosshair.png" id="3"]
[resource]
script = ExtResource("1")
model = ExtResource("2")
position = Vector3(0.4, -0.35, -0.4)
crosshair = ExtResource("3")
rotation = Vector3(0, 180, 0)
muzzle_position = Vector3(0.05, -0.3, 0.8)
cooldown = 0.12
max_distance = 15
damage = 30
spread = 0.4
shot_count = 1
knockback = 18
min_knockback = Vector2(0.025, 0.025)
max_knockback = Vector2(0.045, 0.04)
sound_shoot = "assets/kenney-fps/sounds/blaster.ogg"
+8
View File
@@ -0,0 +1,8 @@
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://assets/quaternius/models/Rifle.glb" id="1"]
[node name="Rifle" type="Node3D"]
scale = Vector3(0.07, 0.07, 0.07)
[node name="Model" parent="." instance=ExtResource("1")]
+22
View File
@@ -0,0 +1,22 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=4 format=3]
[ext_resource type="Script" path="res://assets/kenney-fps/scripts/weapon.gd" id="1"]
[ext_resource type="PackedScene" path="res://assets/quaternius/weapons/shotgun.tscn" id="2"]
[ext_resource type="Texture2D" uid="uid://2jld33y6h5pq" path="res://assets/kenney-fps/sprites/crosshair.png" id="3"]
[resource]
script = ExtResource("1")
model = ExtResource("2")
position = Vector3(0.4, -0.35, -0.4)
crosshair = ExtResource("3")
rotation = Vector3(0, 180, 0)
muzzle_position = Vector3(0.05, -0.3, 0.8)
cooldown = 0.8
max_distance = 15
damage = 20
spread = 1.5
shot_count = 6
knockback = 30
min_knockback = Vector2(0.025, 0.025)
max_knockback = Vector2(0.045, 0.04)
sound_shoot = "assets/kenney-fps/sounds/blaster.ogg"
+8
View File
@@ -0,0 +1,8 @@
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://assets/quaternius/models/Shotgun.glb" id="1"]
[node name="Shotgun" type="Node3D"]
scale = Vector3(0.08, 0.08, 0.08)
[node name="Model" parent="." instance=ExtResource("1")]
@@ -0,0 +1,22 @@
[gd_resource type="Resource" script_class="Weapon" load_steps=4 format=3]
[ext_resource type="Script" path="res://assets/kenney-fps/scripts/weapon.gd" id="1"]
[ext_resource type="PackedScene" path="res://assets/quaternius/weapons/sniperrifle.tscn" id="2"]
[ext_resource type="Texture2D" uid="uid://ce3lgq7foiusl" path="res://assets/kenney-fps/sprites/crosshair-repeater.png" id="3"]
[resource]
script = ExtResource("1")
model = ExtResource("2")
position = Vector3(0.45, -0.3, -0.3)
crosshair = ExtResource("3")
rotation = Vector3(0, 180, 0)
muzzle_position = Vector3(0.05, -0.25, 0.8)
cooldown = 1.2
max_distance = 15
damage = 90
spread = 0.1
shot_count = 1
knockback = 35
min_knockback = Vector2(0.025, 0.025)
max_knockback = Vector2(0.045, 0.04)
sound_shoot = "assets/kenney-fps/sounds/blaster_repeater.ogg"
@@ -0,0 +1,8 @@
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://assets/quaternius/models/SniperRifle.glb" id="1"]
[node name="SniperRifle" type="Node3D"]
scale = Vector3(0.07, 0.07, 0.07)
[node name="Model" parent="." instance=ExtResource("1")]
@@ -21,6 +21,12 @@ var _slot_2_latch := false
var _slot_3_latch := false
var _slot_4_latch := false
# Direct keyboard state (bypasses Input.get_axis() for reliability)
var _key_w := false
var _key_s := false
var _key_a := false
var _key_d := false
# Input variables (frame-rate mouse accumulator)
var mouse_rotation: Vector2 = Vector2.ZERO
@@ -43,6 +49,7 @@ func _notification(what):
if what == NOTIFICATION_WM_WINDOW_FOCUS_IN:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
override_mouse = false
print("[INPUT] Window focus gained — recaptured mouse")
func _input(event: InputEvent) -> void:
if !is_multiplayer_authority(): return
@@ -73,14 +80,36 @@ func _input(event: InputEvent) -> void:
if event.is_action_pressed("weapon_slot_4"):
_slot_4_latch = true
# Track WASD directly from key events (backup for Input.get_axis())
if event is InputEventKey and event.keycode != 0:
match event.keycode:
KEY_W: _key_w = event.pressed
KEY_S: _key_s = event.pressed
KEY_A: _key_a = event.pressed
KEY_D: _key_d = event.pressed
# Also check physical keycode as fallback
match event.physical_keycode:
KEY_W: _key_w = event.pressed
KEY_S: _key_s = event.pressed
KEY_A: _key_a = event.pressed
KEY_D: _key_d = event.pressed
func _gather():
if !is_setup:
print("[INPUT DEBUG] _gather first call - setting up")
setup()
# Movement (continuous)
# Movement: try Input.get_axis() first, fall back to direct key tracking
var mx = Input.get_axis("move_west", "move_east")
var mz = Input.get_axis("move_north", "move_south")
# If Input.get_axis() returns 0 but keys are pressed via event tracking, use event tracking
if mx == 0.0 and mz == 0.0:
if _key_d: mx = 1.0
elif _key_a: mx = -1.0
if _key_s: mz = 1.0
elif _key_w: mz = -1.0
var old_mv = movement
movement = Vector3(mx, 0, mz)
if movement != old_mv and (mx != 0 or mz != 0):
@@ -131,3 +160,7 @@ func setup():
big_gun.hide()
hud.show()
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
# Ensure window has keyboard focus for WASD
get_window().call_deferred("grab_focus")
DisplayServer.call_deferred("window_move_to_foreground")
print("[INPUT] Setup complete — camera active, mouse captured, focus grabbed")
@@ -94,8 +94,9 @@ func _handle_new_peer(id: int):
if bomb:
bomb.sync_to_peer(id)
if avatars.size() <= 2:
# First opponent joined — restart round so both spawn alive
round_manager.start_round()
# Dedicated server handles match start via headless_server.gd
if not _dedicated_server:
round_manager.start_round()
else:
# 3+ players: sync state, spectate until next round
round_manager.sync_to_peer(id)
@@ -173,6 +174,15 @@ func _spawn(id: int):
if rollback_sync and rollback_sync.has_method("process_settings"):
rollback_sync.process_settings()
# On the client side, initialize weapon loadout locally so the player
# has weapons immediately. The server will reconcile via rollback state sync.
if not multiplayer.is_server():
var weapon_mgr = avatar.find_child("WeaponManager")
if weapon_mgr and weapon_mgr.has_method("set_default_loadout"):
# Default to T team — server will correct via state sync
weapon_mgr.set_default_loadout(1) # TeamManager.Team.T = 1
print("[Spawner] Client-side weapon loadout initialized for peer %s" % id)
func get_team_spawn_point(peer_id: int, spawn_idx: int = 0) -> Vector3:
var team := team_manager.get_team(peer_id)
var points: Array[Marker3D]
@@ -129,6 +129,19 @@ func _process(_delta: float):
print("[PLAYER DEBUG] _process skipping — input not authority (unique_id=%d, input_auth=%d)" % [multiplayer.get_unique_id(), input.get_multiplayer_authority()])
return
# DEBUG: Show input state in window title
if _is_local and input:
var mx := Input.get_axis("move_west", "move_east")
var mz := Input.get_axis("move_north", "move_south")
var dead := death_tick >= 0
var frozen := round_manager != null and round_manager.freeze_end_tick >= 0 and NetworkTime.tick < round_manager.freeze_end_tick
var hp_str := str(health)
var rm_ok := round_manager != null
# Weapon model load status from weapon_manager
var knife_ok := weapon_manager != null and weapon_manager.has_knife_model()
var gun_ok := weapon_manager != null and weapon_manager.has_gun_model()
get_window().title = "TS [mv=%.1f,%.1f] hp=%s d=%s f=%s k=%s g=%s [fire=%s]" % [mx, mz, hp_str, dead, frozen, knife_ok, gun_ok, input.fire_held]
# Smooth position interpolation between ticks
var f := NetworkTime.tick_factor
global_position = _lerp_from_pos.lerp(_lerp_to_pos, f)
@@ -8,7 +8,7 @@ signal round_started(round_number: int)
signal round_ended(winner: TeamManager.Team)
signal player_killed(victim_id: int, killer_id: int, weapon_id: int, kill_tick: int)
@export var freeze_time: float = 5.0
@export var freeze_time: float = 0.0
@export var round_time: float = 115.0
@export var round_end_time: float = 5.0
@export var max_rounds: int = 24
@@ -26,6 +26,7 @@ var _pending_grenades: Array[Dictionary] = [] # Buffered throws, consumed in _a
# Weapon models (loaded at runtime)
var _knife_model: Node3D
var _pistol_model: Node3D
var _gun_model: Node3D
var _grenade_model: Node3D
var _current_visible_slot: int = -1
@@ -40,17 +41,61 @@ func _ready():
_load_weapon_models()
func _load_weapon_models():
# Knife model
var knife_scene := load("res://examples/multiplayer-fps/models/knife.fbx") as PackedScene
if knife_scene:
_knife_model = knife_scene.instantiate()
_knife_model.scale = Vector3(0.003, 0.003, 0.003)
_knife_model.position = Vector3(0.15, -0.22, -0.35)
_knife_model.rotation_degrees = Vector3(-90, 180, 0)
add_child(_knife_model)
# Weapon models from Quaternius FPS Pack — GLB format, reliable import
# Slot 0 (melee) + Slot 1 (pistol): Use Quaternius Pistol model
var pistol_scene := load("res://assets/quaternius/models/Pistol.glb") as PackedScene
if pistol_scene:
# Slot 0: small pistol as melee visual
var knife = pistol_scene.instantiate()
knife.scale = Vector3(0.35, 0.35, 0.35)
knife.position = Vector3(0.08, -0.18, -0.28)
knife.rotation_degrees = Vector3(-90, 180, 0)
add_child(knife)
_knife_model = knife
_knife_model.hide()
# Slot 1: same pistol model, slightly different position
var pistol = pistol_scene.instantiate()
pistol.scale = Vector3(0.35, 0.35, 0.35)
pistol.position = Vector3(0.08, -0.18, -0.28)
pistol.rotation_degrees = Vector3(-90, 180, 0)
add_child(pistol)
_pistol_model = pistol
_pistol_model.hide()
else:
print("[WM] Pistol model not available — using placeholder")
for slot in [0, 1]:
var box := MeshInstance3D.new()
box.mesh = BoxMesh.new()
box.mesh.size = Vector3(0.3, 0.05, 0.02)
box.position = Vector3(0.15, -0.22, -0.35)
box.rotation_degrees = Vector3(-90, 0, 0)
add_child(box)
if slot == 0:
_knife_model = box
else:
_pistol_model = box
box.hide()
# Grenade model
# Slot 2 (primary rifle): Use Quaternius Rifle model
var rifle_scene := load("res://assets/quaternius/models/Rifle.glb") as PackedScene
if not rifle_scene:
rifle_scene = load("res://assets/quaternius/models/P90.glb") as PackedScene
if rifle_scene:
var rifle = rifle_scene.instantiate()
rifle.scale = Vector3(0.35, 0.35, 0.35)
rifle.position = Vector3(0.06, -0.16, -0.25)
rifle.rotation_degrees = Vector3(-90, 180, 0)
add_child(rifle)
if _gun_model:
_gun_model.hide()
_gun_model = rifle
_gun_model.hide()
else:
print("[WM] Rifle model not available — using placeholder TinyGun")
# Grenade model (keep existing)
var grenade_scene := load("res://examples/multiplayer-fps/models/grenade.glb") as PackedScene
if grenade_scene:
_grenade_model = grenade_scene.instantiate()
@@ -59,21 +104,7 @@ func _load_weapon_models():
add_child(_grenade_model)
_grenade_model.hide()
# Gun model — replace placeholder box with Kenney blaster
var gun_scene := load("res://assets/kenney/models/blaster.glb") as PackedScene
if gun_scene:
var blaster = gun_scene.instantiate()
blaster.scale = Vector3(0.003, 0.003, 0.003)
blaster.position = Vector3(0.09, -0.18, -0.3)
blaster.rotation_degrees = Vector3(-90, 180, 0)
add_child(blaster)
# Hide the old box mesh, use blaster instead
if _gun_model:
_gun_model.hide()
_gun_model = blaster
_gun_model.hide()
# Knife sound
# Knife slash sound
var slash_stream := load("res://examples/multiplayer-fps/sounds/knife/knife_slash1.wav")
if slash_stream:
_knife_sound = AudioStreamPlayer3D.new()
@@ -87,14 +118,18 @@ func _process(_delta: float):
_current_visible_slot = active_slot
if _knife_model:
_knife_model.visible = (active_slot == 0)
if _pistol_model:
_pistol_model.visible = (active_slot == 1)
if _gun_model:
_gun_model.visible = (active_slot == 1 or active_slot == 2)
_gun_model.visible = (active_slot == 2)
if _grenade_model:
_grenade_model.visible = (active_slot == 3)
func hide_weapons():
if _knife_model:
_knife_model.hide()
if _pistol_model:
_pistol_model.hide()
if _gun_model:
_gun_model.hide()
if _grenade_model:
@@ -104,6 +139,12 @@ func hide_weapons():
func show_weapons():
_current_visible_slot = -1 # Force refresh on next _process
# Debug helpers for window title
func has_knife_model() -> bool:
return _knife_model != null
func has_gun_model() -> bool:
return _gun_model != null
func get_active_weapon() -> WeaponData:
var wid := inventory[active_slot]
if wid == 0:
@@ -158,12 +199,11 @@ func give_weapon(weapon_id: int):
NetworkRollback.mutate(self)
func _rollback_tick(delta: float, tick: int, _is_fresh: bool):
if rollback_synchronizer.is_predicting():
return
# DEBUG: weapon switching
if input and (input.slot_1 or input.slot_2 or input.slot_3 or input.slot_4):
print("[WEAPON DEBUG] _rollback_tick tick=%d slots=[s1=%s s2=%s s3=%s s4=%s] active=%d inv=%s" % [tick, input.slot_1, input.slot_2, input.slot_3, input.slot_4, active_slot, inventory])
# NOTE: On the client, the player CharacterBody3D is owned by the server
# (peer 1), so `rollback_synchronizer.is_predicting()` may return true
# on early ticks when input history hasn't been built up yet.
# We process weapon actions anyway — the server reconciles via rollback.
# Only skip if the player is dead or frozen (checked below).
# Skip if player dead
if player.death_tick >= 0 and tick >= player.death_tick:
@@ -410,8 +450,3 @@ func _spawn_grenade(origin: Vector3, direction: Vector3, owner_id: int, type_idx
get_tree().current_scene.add_child(grenade)
var gtype: Grenade.GrenadeType = Grenade.GrenadeType.FLASH if type_idx == 0 else Grenade.GrenadeType.SMOKE
grenade.setup(origin, direction, owner_id, gtype)
func _rollback_tick(delta: float, tick: int, _is_fresh: bool):
# DEBUG: Log when this is called with non-default input
if input and (input.slot_1 or input.slot_2 or input.slot_3 or input.slot_4):
print("[WEAPON DEBUG] _rollback_tick tick=%d slots=[%s %s %s %s]" % [tick, input.slot_1, input.slot_2, input.slot_3, input.slot_4])
+14 -4
View File
@@ -72,24 +72,34 @@ func join():
print("Client started")
connect_ui.hide()
# Ensure game window has keyboard focus for input
get_window().grab_focus()
# Force window focus for keyboard input (Windows GUI apps need this)
_ensure_input_focus()
# Only start manually if NetworkEvents is not handling it (e.g. multiplayer-simple)
if not NetworkEvents.enabled:
NetworkTime.start()
func _ensure_input_focus() -> void:
## Force the game window to have keyboard focus and captured mouse.
## On Windows GUI exports, the window may not hold keyboard focus after
## the connection UI is dismissed, causing Input.get_axis() to return 0.
DisplayServer.window_move_to_foreground()
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
# Deferred retry after window settles
get_window().call_deferred("grab_focus")
Input.call_deferred("set_mouse_mode", Input.MOUSE_MODE_CAPTURED)
func _enter_tree():
# Hide and show UI as appropriate
# These handlers are necessary, since the game could have started via
# autoconnect, or any other method
NetworkEvents.on_client_start.connect(func(__):
connect_ui.hide()
get_window().grab_focus()
_ensure_input_focus()
)
NetworkEvents.on_server_start.connect(func():
connect_ui.hide()
get_window().grab_focus()
_ensure_input_focus()
)
NetworkEvents.on_client_stop.connect(func(): connect_ui.show())
NetworkEvents.on_server_stop.connect(func(): connect_ui.show())
+5 -5
View File
@@ -18,15 +18,15 @@ patch_delta_exclude_filters=""
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=true
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
script_export_mode=1
[preset.0.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=0
debug/export_console_wrapper=2
binary_format/embed_pck=true
binary_format/architecture="x86_64"
texture_format/s3tc_bptc=true
@@ -69,9 +69,9 @@ patch_delta_exclude_filters=""
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=true
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
script_export_mode=1
[preset.1.options]
Binary file not shown.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025-2026 Owen Sterling
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+168
View File
@@ -0,0 +1,168 @@
# Godot MCP Runtime
<p align="center">
<a href="https://glama.ai/mcp/servers/@Erodenn/godot-mcp-runtime"><img width="380" height="200" src="https://glama.ai/mcp/servers/@Erodenn/godot-runtime-mcp/badge" alt="godot-runtime-mcp MCP server"></a>
</p>
<p align="center">
<a href="https://modelcontextprotocol.io/introduction"><img src="https://badge.mcpx.dev?type=server" alt="MCP Server"></a>
<a href="https://www.npmjs.com/package/godot-mcp-runtime"><img src="https://img.shields.io/npm/v/godot-mcp-runtime" alt="npm version"></a>
<a href="https://www.npmjs.com/package/godot-mcp-runtime"><img src="https://img.shields.io/npm/dt/godot-mcp-runtime" alt="npm downloads"></a>
<a href="LICENSE"><img src="https://badgen.net/github/license/Erodenn/godot-mcp-runtime" alt="License: MIT"></a>
<a href="https://nodejs.org/"><img src="https://img.shields.io/node/v/godot-mcp-runtime" alt="Node.js"></a>
</p>
A lightweight [MCP](https://modelcontextprotocol.io/) server that pairs comprehensive headless editing with full runtime control over a [Godot](https://godotengine.org/) 4.x project. Scene, node, autoload, and validation ops cover everything short of the most niche corners of the engine; the runtime bridge adds screenshots, input simulation, UI discovery, and live GDScript against the running scene tree.
<p align="center"><img src="docs/assets/demo.gif" alt="Agent driving a Godot game via MCP runtime tools" width="1000"></p>
<h3 align="center">The AI doesn't just write your game, it can check its work.</h3>
<br>
- **Headless editing** — scenes, nodes, scripts, signals, validation, no editor window
- **Runtime control** — screenshots, input simulation, UI discovery, and live GDScript against the running game
- **Zero footprint** — no Godot addon, no project commits, auto-cleanup on shutdown
**No addon required.** Most Godot MCP servers that offer runtime support ship as a Godot addon, something you install into your project, commit to version control, and manage as a dependency. Use npx and there's no install or setup needed.
Think of it as [Playwright MCP](https://github.com/microsoft/playwright-mcp), but for Godot. This does the same thing for games: run the project, take a screenshot, simulate input, read what's on screen, execute a script against the live scene tree. The agent closes the loop on its own changes rather than handing off to you to verify.
> [!NOTE]
> This is not a playtesting replacement. It doesn't catch the subtle feel issues that only a human notices, and it won't tell you if your game is fun. What it does is let an agent confirm that a scene loads, a button responds, a value updated, a script ran without errors. The ability to check work is crucial for AI driven workflows.
## Contents
- [What It Does](#what-it-does)
- [Quick Start](#quick-start)
- [Docs](#docs)
- [Acknowledgments](#acknowledgments)
- [License](#license)
## What It Does
**Built for agents.** Every tool is purpose-built and self-documenting. When something fails, the response tells the agent how to fix it; when something succeeds, it points toward the next step. The result is an AI that stays unstuck and self-corrects without needing you to nudge it along.
**Headless editing.** Create scenes, add nodes, set properties, attach scripts, connect signals, validate GDScript. All the standard operations, no editor window required.
**Runtime bridge.** When `run_project` or `attach_project` is called, the server injects `McpBridge` as an autoload. This opens a localhost-only TCP listener (both auto-select a free port when `bridgePort` is omitted; pass `bridgePort` to pin a specific port) and enables:
- **Screenshots:** Capture the viewport — by default returns a 960x540 preview inline plus the full PNG on disk; use `responseMode: 'full'` for pixel-perfect or `'path_only'` to skip the inline image
- **Input simulation:** Batched sequences of key presses, mouse clicks, mouse motion, UI element clicks by name or path, Godot action events, and timed waits
- **UI discovery:** Walk the live scene tree and collect every visible Control node with its position, type, text content, and disabled state
- **Live script execution:** Compile and run arbitrary GDScript with full SceneTree access while the game is running
**Background mode.** Pass `background: true` to `run_project` and the Godot window moves off-screen (positioned at `(-9999, -9999)`) with physical input blocked: borderless, unfocusable, mouse-passthrough. Programmatic input, screenshots, and all runtime tools work exactly the same. Useful for automated agent-driven testing where the window shouldn't be visible or interactive.
**Manual attach mode.** When something other than MCP launches the game (a CI pipeline, an external debugger, your own shell), call `attach_project` first. It injects the bridge and marks the project active without spawning Godot, so when you launch the game manually, runtime tools work against it. Use `detach_project` when done.
> [!IMPORTANT]
> `get_debug_output` is unavailable in attached mode. stdout and stderr only flow through processes MCP started itself, so when Godot is launched externally there's no captured output to return. Use `run_project` if you need the debug stream.
The bridge cleans itself up automatically when `stop_project` or `detach_project` is called. No leftover autoloads, no modified project files.
## Quick Start
### Prerequisites
- [Node.js](https://nodejs.org/) v20+
- [Godot 4.x](https://godotengine.org/)
That's it. No Godot addon, no project modifications.
### Configure Your MCP Client
Add the following to your MCP client config. Works with Claude Code, Claude Desktop, Cursor, or any MCP-compatible client.
**Zero-install via npx (recommended):**
```json
{
"mcpServers": {
"godot": {
"command": "npx",
"args": ["-y", "godot-mcp-runtime"],
"env": {
"GODOT_PATH": "<path-to-godot-executable>"
}
}
}
}
```
**Or install globally:**
```bash
npm install -g godot-mcp-runtime
```
```json
{
"mcpServers": {
"godot": {
"command": "godot-mcp-runtime",
"env": {
"GODOT_PATH": "<path-to-godot-executable>"
}
}
}
}
```
**Or clone from source:**
```bash
git clone https://github.com/Erodenn/godot-mcp-runtime.git
cd godot-mcp-runtime
npm install
npm run build
```
```json
{
"mcpServers": {
"godot": {
"command": "node",
"args": ["<path-to>/godot-mcp-runtime/dist/index.js"],
"env": {
"GODOT_PATH": "<path-to-godot-executable>"
}
}
}
}
```
> [!TIP]
> **Prefer pnpm?** All three install paths work with pnpm. Substitute `pnpm dlx godot-mcp-runtime` for `npx -y godot-mcp-runtime`, `pnpm add -g godot-mcp-runtime` for the global install, or `pnpm install && pnpm run build` for the source build. pnpm ships stronger defaults against npm supply-chain attacks; see [pnpm's supply chain security guide](https://pnpm.io/supply-chain-security).
If Godot is on your `PATH`, you can omit `GODOT_PATH` entirely. The server will auto-detect it. Set `"DEBUG": "true"` in `env` for verbose logging.
> [!IMPORTANT]
> **Windows path gotchas.** `GODOT_PATH` must point at the Godot executable itself, not its install folder. Backslashes in JSON must be escaped or replaced with forward slashes:
>
> ```json
> "GODOT_PATH": "D:\\Godot\\Godot_v4.4-stable_win64.exe"
> // or equivalently
> "GODOT_PATH": "D:/Godot/Godot_v4.4-stable_win64.exe"
> ```
>
> Setting the variable from a wrapper `.bat` does not propagate to the MCP server — the path must live in the client's `env` block above.
### Verify
Ask your AI assistant to call `get_project_info`. If it returns a Godot version string (e.g., `4.4.stable`), you're connected and working.
## Docs
- [`docs/tools.md`](docs/tools.md) — full tool reference, grouped by category
- [`docs/tool-authoring.md`](docs/tool-authoring.md) — standards for adding or modifying tools
- [`docs/architecture.md`](docs/architecture.md) — source layout, bridge sequence diagram, lifecycle steps, runtime artifact behavior
## Acknowledgments
Built on the foundation laid by [Coding-Solo/godot-mcp](https://github.com/Coding-Solo/godot-mcp) for headless Godot operations.
Developed with [Claude Code](https://claude.ai/code).
## License
[MIT](LICENSE)
+16
View File
@@ -0,0 +1,16 @@
/**
* Tool dispatch table.
*
* Maps every MCP tool name to a handler that takes the runner + raw args and
* returns the tool response. Extracted from index.ts so tests can exercise
* dispatch as a pure data structure (no Server / stdio / lifecycle setup).
*
* Behavioral contract preserved from the original switch in index.ts:
* - Each name routes to the same handler it did before.
* - Unknown tool names throw McpError(MethodNotFound, ...) — see
* `dispatchToolCall`.
*/
import type { GodotRunner, OperationParams, ToolHandler, ToolResponse } from './utils/godot-runner.js';
export declare const toolDispatch: Record<string, ToolHandler>;
export declare function dispatchToolCall(runner: GodotRunner, toolName: string, args: OperationParams): Promise<ToolResponse>;
//# sourceMappingURL=dispatch.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,WAAW,EACX,YAAY,EACb,MAAM,yBAAyB,CAAC;AAsDjC,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CA4CpD,CAAC;AAEF,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,YAAY,CAAC,CAMvB"}
+69
View File
@@ -0,0 +1,69 @@
/**
* Tool dispatch table.
*
* Maps every MCP tool name to a handler that takes the runner + raw args and
* returns the tool response. Extracted from index.ts so tests can exercise
* dispatch as a pure data structure (no Server / stdio / lifecycle setup).
*
* Behavioral contract preserved from the original switch in index.ts:
* - Each name routes to the same handler it did before.
* - Unknown tool names throw McpError(MethodNotFound, ...) — see
* `dispatchToolCall`.
*/
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
import { handleLaunchEditor, handleRunProject, handleAttachProject, handleDetachProject, handleGetDebugOutput, handleStopProject, handleTakeScreenshot, handleSimulateInput, handleGetUiElements, handleRunScript, } from './tools/runtime-tools.js';
import { handleListAutoloads, handleAddAutoload, handleRemoveAutoload, handleUpdateAutoload, } from './tools/autoload-tools.js';
import { handleListProjects, handleGetProjectInfo, handleGetProjectFiles, handleSearchProject, handleGetSceneDependencies, handleGetProjectSettings, } from './tools/project-tools.js';
import { handleCreateScene, handleAddNode, handleLoadSprite, handleSaveScene, handleExportMeshLibrary, handleBatchSceneOperations, } from './tools/scene-tools.js';
import { handleDeleteNodes, handleSetNodeProperties, handleGetNodeProperties, handleAttachScript, handleGetSceneTree, handleDuplicateNode, handleGetNodeSignals, handleConnectSignal, handleDisconnectSignal, } from './tools/node-tools.js';
import { handleValidate } from './tools/validate-tools.js';
export const toolDispatch = {
// Project tools
launch_editor: handleLaunchEditor,
run_project: handleRunProject,
attach_project: handleAttachProject,
detach_project: handleDetachProject,
get_debug_output: handleGetDebugOutput,
stop_project: handleStopProject,
list_projects: (_runner, args) => handleListProjects(args),
get_project_info: handleGetProjectInfo,
take_screenshot: handleTakeScreenshot,
simulate_input: handleSimulateInput,
get_ui_elements: handleGetUiElements,
run_script: handleRunScript,
list_autoloads: (_runner, args) => handleListAutoloads(args),
add_autoload: (_runner, args) => handleAddAutoload(args),
remove_autoload: (_runner, args) => handleRemoveAutoload(args),
update_autoload: (_runner, args) => handleUpdateAutoload(args),
get_project_files: (_runner, args) => handleGetProjectFiles(args),
search_project: (_runner, args) => handleSearchProject(args),
get_scene_dependencies: (_runner, args) => handleGetSceneDependencies(args),
get_project_settings: (_runner, args) => handleGetProjectSettings(args),
// Scene tools
create_scene: handleCreateScene,
add_node: handleAddNode,
load_sprite: handleLoadSprite,
save_scene: handleSaveScene,
export_mesh_library: handleExportMeshLibrary,
batch_scene_operations: handleBatchSceneOperations,
// Node tools
delete_nodes: handleDeleteNodes,
set_node_properties: handleSetNodeProperties,
get_node_properties: handleGetNodeProperties,
attach_script: handleAttachScript,
get_scene_tree: handleGetSceneTree,
duplicate_node: handleDuplicateNode,
get_node_signals: handleGetNodeSignals,
connect_signal: handleConnectSignal,
disconnect_signal: handleDisconnectSignal,
// Validate tools
validate: handleValidate,
};
export async function dispatchToolCall(runner, toolName, args) {
const handler = toolDispatch[toolName];
if (!handler) {
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${toolName}`);
}
return await handler(runner, args);
}
//# sourceMappingURL=dispatch.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AASzE,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,GAChB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,CAAC,MAAM,YAAY,GAAgC;IACvD,gBAAgB;IAChB,aAAa,EAAE,kBAAkB;IACjC,WAAW,EAAE,gBAAgB;IAC7B,cAAc,EAAE,mBAAmB;IACnC,cAAc,EAAE,mBAAmB;IACnC,gBAAgB,EAAE,oBAAoB;IACtC,YAAY,EAAE,iBAAiB;IAC/B,aAAa,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC;IAC1D,gBAAgB,EAAE,oBAAoB;IACtC,eAAe,EAAE,oBAAoB;IACrC,cAAc,EAAE,mBAAmB;IACnC,eAAe,EAAE,mBAAmB;IACpC,UAAU,EAAE,eAAe;IAC3B,cAAc,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC;IAC5D,YAAY,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC;IACxD,eAAe,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC;IAC9D,eAAe,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC;IAC9D,iBAAiB,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC;IACjE,cAAc,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC;IAC5D,sBAAsB,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC;IAC3E,oBAAoB,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC;IAEvE,cAAc;IACd,YAAY,EAAE,iBAAiB;IAC/B,QAAQ,EAAE,aAAa;IACvB,WAAW,EAAE,gBAAgB;IAC7B,UAAU,EAAE,eAAe;IAC3B,mBAAmB,EAAE,uBAAuB;IAC5C,sBAAsB,EAAE,0BAA0B;IAElD,aAAa;IACb,YAAY,EAAE,iBAAiB;IAC/B,mBAAmB,EAAE,uBAAuB;IAC5C,mBAAmB,EAAE,uBAAuB;IAC5C,aAAa,EAAE,kBAAkB;IACjC,cAAc,EAAE,kBAAkB;IAClC,cAAc,EAAE,mBAAmB;IACnC,gBAAgB,EAAE,oBAAoB;IACtC,cAAc,EAAE,mBAAmB;IACnC,iBAAiB,EAAE,sBAAsB;IAEzC,iBAAiB;IACjB,QAAQ,EAAE,cAAc;CACzB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAmB,EACnB,QAAgB,EAChB,IAAqB;IAErB,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,iBAAiB,QAAQ,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,MAAM,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC"}
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env node
/**
* Godot MCP Server
*
* This MCP server provides tools for interacting with the Godot game engine.
* It enables AI assistants to launch the Godot editor, run Godot projects,
* capture debug output, manipulate scenes and nodes, and more.
*/
export declare const allToolDefinitions: import("./utils/godot-runner.js").ToolDefinition[];
export declare const serverInstructions = "Godot MCP Server \u2014 AI-driven Godot 4.x project manipulation.\n\nTool categories:\n- Project management: launch_editor, run_project, attach_project, detach_project, stop_project, get_debug_output, list_projects, get_project_info\n- Scene editing (headless): create_scene, add_node, load_sprite, save_scene, export_mesh_library, batch_scene_operations\n- Node editing (headless): delete_nodes, set_node_properties, get_node_properties, attach_script, get_scene_tree, duplicate_node, get_node_signals, connect_signal, disconnect_signal\n- Runtime (requires run_project or attach_project): take_screenshot, simulate_input, get_ui_elements, run_script\n- Project config (no Godot process): list_autoloads, add_autoload, remove_autoload, update_autoload, get_project_files, search_project, get_scene_dependencies, get_project_settings\n- Validation: validate\n\nKey behaviors:\n- All mutation operations (add_node, set_node_properties, delete_nodes, etc.) save the scene automatically. Only use save_scene for save-as (newPath) or re-canonicalization.\n- Headless Godot initializes ALL registered autoloads. If any autoload is broken, headless operations will fail. Use list_autoloads / remove_autoload to diagnose.\n- run_project verifies bridge readiness before returning success. If it reports degraded status, retry runtime tools after a moment or check get_debug_output.\n- attach_project is the fallback path for a manually launched Godot process. It injects the bridge and marks the project active, but it does not spawn Godot or capture stdout/stderr.\n- click_element in simulate_input resolves by node path or node name (BFS search), NOT by visible text. Use get_ui_elements to discover valid element identifiers.\n- run_script expects GDScript with \"extends RefCounted\" and \"func execute(scene_tree: SceneTree) -> Variant\".\n- run_project spawns Godot without -d so runtime errors do not pause execution; the `breakpoint` keyword in user code is a no-op (no debugger is attached). SCRIPT ERROR output and GDScript backtraces still appear in stderr.";
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAkBH,eAAO,MAAM,kBAAkB,oDAO9B,CAAC;AAEF,eAAO,MAAM,kBAAkB,mhEAiBmM,CAAC"}
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env node
/**
* Godot MCP Server
*
* This MCP server provides tools for interacting with the Godot game engine.
* It enables AI assistants to launch the Godot editor, run Godot projects,
* capture debug output, manipulate scenes and nodes, and more.
*/
// Lower-level `Server` is deliberate; see CONTRIBUTING.md "MCP SDK: Server vs McpServer".
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { GodotRunner, getErrorMessage } from './utils/godot-runner.js';
import { dispatchToolCall } from './dispatch.js';
import { runtimeToolDefinitions } from './tools/runtime-tools.js';
import { autoloadToolDefinitions } from './tools/autoload-tools.js';
import { projectToolDefinitions } from './tools/project-tools.js';
import { sceneToolDefinitions } from './tools/scene-tools.js';
import { nodeToolDefinitions } from './tools/node-tools.js';
import { validateToolDefinitions } from './tools/validate-tools.js';
export const allToolDefinitions = [
...runtimeToolDefinitions,
...autoloadToolDefinitions,
...projectToolDefinitions,
...sceneToolDefinitions,
...nodeToolDefinitions,
...validateToolDefinitions,
];
export const serverInstructions = `Godot MCP Server — AI-driven Godot 4.x project manipulation.
Tool categories:
- Project management: launch_editor, run_project, attach_project, detach_project, stop_project, get_debug_output, list_projects, get_project_info
- Scene editing (headless): create_scene, add_node, load_sprite, save_scene, export_mesh_library, batch_scene_operations
- Node editing (headless): delete_nodes, set_node_properties, get_node_properties, attach_script, get_scene_tree, duplicate_node, get_node_signals, connect_signal, disconnect_signal
- Runtime (requires run_project or attach_project): take_screenshot, simulate_input, get_ui_elements, run_script
- Project config (no Godot process): list_autoloads, add_autoload, remove_autoload, update_autoload, get_project_files, search_project, get_scene_dependencies, get_project_settings
- Validation: validate
Key behaviors:
- All mutation operations (add_node, set_node_properties, delete_nodes, etc.) save the scene automatically. Only use save_scene for save-as (newPath) or re-canonicalization.
- Headless Godot initializes ALL registered autoloads. If any autoload is broken, headless operations will fail. Use list_autoloads / remove_autoload to diagnose.
- run_project verifies bridge readiness before returning success. If it reports degraded status, retry runtime tools after a moment or check get_debug_output.
- attach_project is the fallback path for a manually launched Godot process. It injects the bridge and marks the project active, but it does not spawn Godot or capture stdout/stderr.
- click_element in simulate_input resolves by node path or node name (BFS search), NOT by visible text. Use get_ui_elements to discover valid element identifiers.
- run_script expects GDScript with "extends RefCounted" and "func execute(scene_tree: SceneTree) -> Variant".
- run_project spawns Godot without -d so runtime errors do not pause execution; the \`breakpoint\` keyword in user code is a no-op (no debugger is attached). SCRIPT ERROR output and GDScript backtraces still appear in stderr.`;
class GodotMcpServer {
server;
runner;
constructor(config) {
this.runner = new GodotRunner(config);
this.server = new Server({
name: 'godot-mcp',
version: '3.1.2',
}, {
capabilities: {
tools: {},
},
instructions: serverInstructions,
});
this.setupToolHandlers();
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.cleanup();
process.exit(0);
});
process.on('SIGTERM', async () => {
await this.cleanup();
process.exit(0);
});
}
async cleanup() {
console.error('[SERVER] Cleaning up resources');
await this.runner.stopProject();
await this.server.close();
}
setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: allToolDefinitions,
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const toolName = request.params.name;
const args = request.params.arguments || {};
console.error(`[SERVER] Handling tool request: ${toolName}`);
return await dispatchToolCall(this.runner, toolName, args);
});
}
async run() {
try {
await this.runner.detectGodotPath();
const godotPath = this.runner.getGodotPath();
if (godotPath) {
console.error(`[SERVER] Using Godot at: ${godotPath}`);
}
// detectGodotPath() already emits a specific logError on failure (bad
// GODOT_PATH, no binary found, etc.). Don't duplicate with a generic
// warning here — the runner's message names the actual cause.
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Godot MCP server running on stdio');
}
catch (error) {
console.error('[SERVER] Failed to start:', getErrorMessage(error));
process.exit(1);
}
}
}
// Create and run the server
const server = new GodotMcpServer();
server.run().catch((error) => {
console.error('Failed to run server:', getErrorMessage(error));
process.exit(1);
});
//# sourceMappingURL=index.js.map

Some files were not shown because too many files have changed in this diff Show More