Compare commits

..
6 Commits
70 changed files with 9288 additions and 450 deletions
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
Model Information:
* title: Low-Poly AK-74
* source: https://sketchfab.com/3d-models/low-poly-ak-74-b27f9003622a4c7499bfd8d2a06dc47b
* author: TastyTony (https://sketchfab.com/TastyTony)
Model License:
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
* requirements: Author must be credited. Commercial use is allowed.
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
This work is based on "Low-Poly AK-74" (https://sketchfab.com/3d-models/low-poly-ak-74-b27f9003622a4c7499bfd8d2a06dc47b) by TastyTony (https://sketchfab.com/TastyTony) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
Model Information:
* title: Modular Shotgun
* source: https://sketchfab.com/3d-models/modular-shotgun-5c04438b213648ea83007858e2b8c162
* author: Somersby (https://sketchfab.com/Somersby)
Model License:
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
* requirements: Author must be credited. Commercial use is allowed.
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
This work is based on "Modular Shotgun" (https://sketchfab.com/3d-models/modular-shotgun-5c04438b213648ea83007858e2b8c162) by Somersby (https://sketchfab.com/Somersby) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

+5 -5
View File
@@ -20,8 +20,8 @@ func _ready() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
camera = get_node_or_null("Camera3D")
if camera and params:
camera.fov = params.base_fov
_target_fov = params.base_fov
camera.fov = SettingsManager.world_fov
_target_fov = SettingsManager.world_fov
# Explicitly enable callbacks — set_script() at runtime doesn't auto-register them
set_process_input(true)
set_process(true)
@@ -29,7 +29,7 @@ func _ready() -> void:
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
var is_ads = camera and camera.fov < params.base_fov - 5.0
var is_ads = camera and camera.fov < SettingsManager.world_fov - 5.0
var sens = SettingsManager.ads_sensitivity if is_ads else SettingsManager.mouse_sensitivity
# Yaw: rotate the player (parent)
get_parent().rotate_y(-event.relative.x * sens)
@@ -53,12 +53,12 @@ func _process(delta: float) -> void:
return
# ── FOV kick ──────────────────────────────────────────────────────────
_target_fov = params.base_fov
_target_fov = SettingsManager.world_fov
var hspeed := Vector2(player.velocity.x, player.velocity.z).length()
# Smoothly scale FOV based on speed above walking speed
var speed_factor = clampf((hspeed - params.walk_speed) / (params.walk_speed * 0.5), 0.0, 1.0)
_target_fov = lerpf(params.base_fov, params.dash_fov, speed_factor)
_target_fov = lerpf(SettingsManager.world_fov, SettingsManager.world_fov + 20.0, speed_factor)
if _weapon_fov_override > 0.0:
_target_fov = _weapon_fov_override
+4
View File
@@ -22,6 +22,7 @@ var default_bindings = {
"weapon_2": {"type": "key", "code": KEY_2},
"weapon_3": {"type": "key", "code": KEY_3},
"weapon_4": {"type": "key", "code": KEY_4},
"toggle_flashlight": {"type": "key", "code": KEY_T},
}
var current_bindings = {}
@@ -29,6 +30,7 @@ var show_debug_ui: bool = false
var username: String = "Player"
var mouse_sensitivity: float = 0.002
var ads_sensitivity: float = 0.001
var world_fov: float = 90.0
# Video Settings
var display_mode: int = DisplayServer.WINDOW_MODE_WINDOWED
@@ -135,6 +137,7 @@ func _save_settings() -> void:
"username": username,
"mouse_sensitivity": mouse_sensitivity,
"ads_sensitivity": ads_sensitivity,
"world_fov": world_fov,
"display_mode": display_mode,
"resolution_index": resolution_index,
"fps_cap": fps_cap,
@@ -171,6 +174,7 @@ func _load_settings() -> void:
if data.has("show_debug_ui"): show_debug_ui = data["show_debug_ui"]
if data.has("mouse_sensitivity"): mouse_sensitivity = data["mouse_sensitivity"]
if data.has("ads_sensitivity"): ads_sensitivity = data["ads_sensitivity"]
if data.has("world_fov"): world_fov = data["world_fov"]
if data.has("display_mode"): display_mode = data["display_mode"]
if data.has("resolution_index"): resolution_index = data["resolution_index"]
if data.has("fps_cap"): fps_cap = data["fps_cap"]
+15
View File
@@ -8,6 +8,7 @@ signal chain_updated(count: int, bonus: float)
var _machine: MovementStateMachine = null
var head_pivot: Node3D = null # FPSCameraRig node
var camera: Camera3D = null
var flashlight: SpotLight3D = null
var _damage_layer: CanvasLayer = null
# Health and Shield
@@ -82,6 +83,17 @@ func _ready() -> void:
if head_pivot:
camera = head_pivot.get_node_or_null("Camera3D")
if camera:
flashlight = SpotLight3D.new()
flashlight.spot_range = 100.0
flashlight.spot_angle = 35.0
flashlight.light_energy = 5.0
flashlight.light_volumetric_fog_energy = 0.0 # Prevents the blinding fog glare and laggy trails
flashlight.shadow_enabled = false
flashlight.position = Vector3(0, 0, 0)
flashlight.visible = false
camera.add_child(flashlight)
_setup_audio()
_setup_grapple()
@@ -652,6 +664,9 @@ func _physics_process(_delta: float) -> void:
var input_grapple = Input.is_action_pressed("grapple")
var input_grapple_just = Input.is_action_just_pressed("grapple")
if Input.is_action_just_pressed("toggle_flashlight") and is_instance_valid(flashlight):
flashlight.visible = !flashlight.visible
server_receive_inputs.rpc_id(1, raw_input, world_dir, input_jump, input_jump_just, input_crouch, input_dash, input_grapple, input_grapple_just)
var speed = velocity.length()
+7
View File
@@ -102,3 +102,10 @@ scoreboard={
[rendering]
rendering_device/driver.windows="d3d12"
[display]
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
window/size/viewport_width=1920
window/size/viewport_height=1080
+5
View File
@@ -0,0 +1,5 @@
[map]
name="Dust 2"
scene_path="res://scenes/maps/dust2/dust2_level.tscn"
color1=Color(0.9, 0.8, 0.6, 1.0)
color2=Color(0.6, 0.5, 0.3, 1.0)
+465
View File
@@ -0,0 +1,465 @@
[gd_scene load_steps=12 format=3 uid="uid://1feccd321600"]
[ext_resource type="Script" path="res://scenes/maps/level_runtime.gd" id="1_rt"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_grey"]
albedo_color = Color(0.08, 0.09, 0.11, 1)
metallic = 0.7
roughness = 0.15
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_blue"]
albedo_color = Color(0, 0.05, 0.1, 1)
emission_enabled = true
emission = Color(0.1, 0.6, 1.0, 1)
emission_energy_multiplier = 3.5
roughness = 0.4
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_glass"]
albedo_color = Color(0.2, 0.8, 0.9, 0.2)
transparency = 1
roughness = 0.05
metallic = 0.9
emission_enabled = true
emission = Color(0.1, 0.4, 0.8, 1)
emission_energy_multiplier = 0.5
[sub_resource type="Shader" id="Shader_lava"]
code = "shader_type spatial;
uniform sampler2D noise_tex : repeat_enable;
uniform vec4 emission_color : source_color;
varying vec3 world_pos;
void vertex() {
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
}
void fragment() {
vec3 blend = abs(NORMAL);
blend /= dot(blend, vec3(1.0));
vec2 flow = TIME * vec2(0.02, 0.05);
vec2 uvX = world_pos.zy * 0.1 + flow;
vec2 uvY = world_pos.xz * 0.1 + flow;
vec2 uvZ = world_pos.xy * 0.1 + flow;
float nX = texture(noise_tex, uvX).r;
float nY = texture(noise_tex, uvY).r;
float nZ = texture(noise_tex, uvZ).r;
float n = nX * blend.x + nY * blend.y + nZ * blend.z;
float lava = smoothstep(0.3, 0.6, n);
ALBEDO = vec3(0.05, 0.01, 0.0);
EMISSION = emission_color.rgb * lava * 6.0;
}"
[sub_resource type="FastNoiseLite" id="FastNoiseLite_lava"]
noise_type = 2
frequency = 0.05
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_lava"]
seamless = true
noise = SubResource("FastNoiseLite_lava")
[sub_resource type="ShaderMaterial" id="StandardMaterial3D_orange"]
shader = SubResource("Shader_lava")
shader_parameter/emission_color = Color(1.0, 0.4, 0.0, 1)
shader_parameter/noise_tex = SubResource("NoiseTexture2D_lava")
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_xxxx"]
sky_top_color = Color(0.05, 0.08, 0.12, 1)
sky_horizon_color = Color(0.15, 0.2, 0.25, 1)
ground_bottom_color = Color(0.02, 0.03, 0.05, 1)
ground_horizon_color = Color(0.15, 0.2, 0.25, 1)
[sub_resource type="Sky" id="Sky_xxxx"]
sky_material = SubResource("ProceduralSkyMaterial_xxxx")
[sub_resource type="Environment" id="Environment_xxxx"]
background_mode = 2
sky = SubResource("Sky_xxxx")
ambient_light_source = 2
ambient_light_color = Color(0.2, 0.25, 0.35, 1)
ambient_light_energy = 1.0
tonemap_mode = 3
glow_enabled = true
glow_intensity = 1.5
glow_bloom = 0.2
glow_hdr_threshold = 1.0
sdfgi_enabled = true
sdfgi_use_occlusion = true
ssao_enabled = true
ssil_enabled = true
volumetric_fog_enabled = true
volumetric_fog_density = 0.015
volumetric_fog_albedo = Color(0.3, 0.5, 0.8, 1)
volumetric_fog_emission = Color(0.02, 0.05, 0.1, 1)
[node name="MapRoot" type="CSGCombiner3D"]
use_collision = true
script = ExtResource("1_rt")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_xxxx")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.707107, -0.5, 0.5, 0, 0.707107, 0.707107, -0.707107, -0.5, 0.5, 0, 20, 0)
shadow_enabled = true
light_color = Color(0.8, 0.9, 1.0, 1)
light_energy = 0.3
[node name="Spawn1" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 45, 4, 0)
[node name="Spawn2" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45, 4, 0)
[node name="Spawn3" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45, 2, -45)
[node name="Spawn4" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 45, 2, -45)
[node name="Spawn5" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -20, 2, -15)
[node name="Spawn6" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 20, 2, -15)
[node name="Spawn7" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -30, 8, 0)
[node name="Spawn8" type="Marker3D" parent="." groups=["spawn_points"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 30, 8, 0)
[node name="FloorBound" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2, 0)
size = Vector3(120, 4, 120)
material = SubResource("StandardMaterial3D_grey")
[node name="CeilingBound" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 30, 0)
size = Vector3(120, 2, 120)
material = SubResource("StandardMaterial3D_grey")
[node name="WallN" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 14, -60)
size = Vector3(120, 32, 2)
material = SubResource("StandardMaterial3D_grey")
[node name="WallS" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 14, 60)
size = Vector3(120, 32, 2)
material = SubResource("StandardMaterial3D_grey")
[node name="WallE" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 60, 14, 0)
size = Vector3(2, 32, 120)
material = SubResource("StandardMaterial3D_grey")
[node name="WallW" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -60, 14, 0)
size = Vector3(2, 32, 120)
material = SubResource("StandardMaterial3D_grey")
[node name="CenterPlat" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 6, 0)
size = Vector3(20, 2, 20)
material = SubResource("StandardMaterial3D_grey")
[node name="CenterDivot" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 7, 0)
size = Vector3(16, 2, 16)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="CenterNeon" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 6, 0)
size = Vector3(6, 0.5, 6)
material = SubResource("StandardMaterial3D_blue")
[node name="PillarC1" type="CSGCylinder3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8, 2, -8)
radius = 1
height = 10
material = SubResource("StandardMaterial3D_orange")
[node name="PillarC2" type="CSGCylinder3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 2, -8)
radius = 1
height = 10
material = SubResource("StandardMaterial3D_orange")
[node name="PillarC3" type="CSGCylinder3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8, 2, 8)
radius = 1
height = 10
material = SubResource("StandardMaterial3D_orange")
[node name="PillarC4" type="CSGCylinder3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 2, 8)
radius = 1
height = 10
material = SubResource("StandardMaterial3D_orange")
[node name="BridgeN" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 6, -18)
size = Vector3(6, 1, 16)
material = SubResource("StandardMaterial3D_glass")
[node name="BridgeS" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 6, 18)
size = Vector3(6, 1, 16)
material = SubResource("StandardMaterial3D_glass")
[node name="BridgeE" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18, 6, 0)
size = Vector3(16, 1, 6)
material = SubResource("StandardMaterial3D_glass")
[node name="BridgeW" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -18, 6, 0)
size = Vector3(16, 1, 6)
material = SubResource("StandardMaterial3D_glass")
[node name="GrappleAnchor1" type="CSGBox3D" parent="."]
transform = Transform3D(0.7071067811865476, 0, -0.7071067811865476, 0, 1, 0, 0.7071067811865476, 0, 0.7071067811865476, -15, 20, -15)
size = Vector3(2, 2, 2)
material = SubResource("StandardMaterial3D_orange")
[node name="GrappleAnchor2" type="CSGBox3D" parent="."]
transform = Transform3D(0.7071067811865476, 0, -0.7071067811865476, 0, 1, 0, 0.7071067811865476, 0, 0.7071067811865476, 15, 20, 15)
size = Vector3(2, 2, 2)
material = SubResource("StandardMaterial3D_orange")
[node name="GrappleAnchor3" type="CSGBox3D" parent="."]
transform = Transform3D(0.7071067811865476, 0, -0.7071067811865476, 0, 1, 0, 0.7071067811865476, 0, 0.7071067811865476, 15, 20, -15)
size = Vector3(2, 2, 2)
material = SubResource("StandardMaterial3D_orange")
[node name="GrappleAnchor4" type="CSGBox3D" parent="."]
transform = Transform3D(0.7071067811865476, 0, -0.7071067811865476, 0, 1, 0, 0.7071067811865476, 0, 0.7071067811865476, -15, 20, 15)
size = Vector3(2, 2, 2)
material = SubResource("StandardMaterial3D_orange")
[node name="WalkwayN" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 6, -30)
size = Vector3(70, 1, 10)
material = SubResource("StandardMaterial3D_grey")
[node name="WalkwayS" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 6, 30)
size = Vector3(70, 1, 10)
material = SubResource("StandardMaterial3D_grey")
[node name="WalkwayE" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 30, 6, 0)
size = Vector3(10, 1, 70)
material = SubResource("StandardMaterial3D_grey")
[node name="WalkwayW" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -30, 6, 0)
size = Vector3(10, 1, 70)
material = SubResource("StandardMaterial3D_grey")
[node name="WallRun1" type="CSGBox3D" parent="."]
transform = Transform3D(0.984807753012208, 0, 0.17364817766693033, 0, 1, 0, -0.17364817766693033, 0, 0.984807753012208, -30, 8, 40)
size = Vector3(24, 16, 2)
material = SubResource("StandardMaterial3D_orange")
[node name="WallRun2" type="CSGBox3D" parent="."]
transform = Transform3D(0.984807753012208, 0, -0.17364817766693033, 0, 1, 0, 0.17364817766693033, 0, 0.984807753012208, 0, 10, 50)
size = Vector3(24, 16, 2)
material = SubResource("StandardMaterial3D_orange")
[node name="WallRun3" type="CSGBox3D" parent="."]
transform = Transform3D(0.984807753012208, 0, 0.17364817766693033, 0, 1, 0, -0.17364817766693033, 0, 0.984807753012208, 30, 12, 40)
size = Vector3(24, 16, 2)
material = SubResource("StandardMaterial3D_orange")
[node name="TunnelWallBlock" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, -45)
size = Vector3(120, 8, 30)
material = SubResource("StandardMaterial3D_grey")
[node name="TunnelCutMain" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -45)
size = Vector3(120, 5, 10)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="SlideBulkhead1" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -20, 3.5, -45)
size = Vector3(4, 4, 10)
material = SubResource("StandardMaterial3D_blue")
[node name="SlideBulkhead2" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 20, 3.5, -45)
size = Vector3(4, 4, 10)
material = SubResource("StandardMaterial3D_blue")
[node name="TunnelExitW" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -35, 2.5, -35)
size = Vector3(10, 5, 20)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="TunnelExitE" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 35, 2.5, -35)
size = Vector3(10, 5, 20)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="TunnelExitCenter" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, -35)
size = Vector3(10, 5, 20)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="RampNW" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.9366721892483976, 0.35020738125946743, 0, -0.35020738125946743, 0.9366721892483976, -30, 3, -20)
size = Vector3(10, 16, 1)
material = SubResource("StandardMaterial3D_orange")
[node name="RampNE" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.9366721892483976, 0.35020738125946743, 0, -0.35020738125946743, 0.9366721892483976, 30, 3, -20)
size = Vector3(10, 16, 1)
material = SubResource("StandardMaterial3D_orange")
[node name="RampSW" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.9366721892483976, -0.35020738125946743, 0, 0.35020738125946743, 0.9366721892483976, -30, 3, 20)
size = Vector3(10, 16, 1)
material = SubResource("StandardMaterial3D_orange")
[node name="RampSE" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.9366721892483976, -0.35020738125946743, 0, 0.35020738125946743, 0.9366721892483976, 30, 3, 20)
size = Vector3(10, 16, 1)
material = SubResource("StandardMaterial3D_orange")
[node name="RampEN" type="CSGBox3D" parent="."]
transform = Transform3D(0.9366721892483976, 0.35020738125946743, 0, -0.35020738125946743, 0.9366721892483976, 0, 0, 0, 1, 20, 3, -30)
size = Vector3(1, 16, 10)
material = SubResource("StandardMaterial3D_orange")
[node name="RampES" type="CSGBox3D" parent="."]
transform = Transform3D(0.9366721892483976, 0.35020738125946743, 0, -0.35020738125946743, 0.9366721892483976, 0, 0, 0, 1, 20, 3, 30)
size = Vector3(1, 16, 10)
material = SubResource("StandardMaterial3D_orange")
[node name="RampWN" type="CSGBox3D" parent="."]
transform = Transform3D(0.9366721892483976, -0.35020738125946743, 0, 0.35020738125946743, 0.9366721892483976, 0, 0, 0, 1, -20, 3, -30)
size = Vector3(1, 16, 10)
material = SubResource("StandardMaterial3D_orange")
[node name="RampWS" type="CSGBox3D" parent="."]
transform = Transform3D(0.9366721892483976, -0.35020738125946743, 0, 0.35020738125946743, 0.9366721892483976, 0, 0, 0, 1, -20, 3, 30)
size = Vector3(1, 16, 10)
material = SubResource("StandardMaterial3D_orange")
[node name="EastBase" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 45, 4, 0)
size = Vector3(20, 8, 30)
material = SubResource("StandardMaterial3D_grey")
[node name="EastBaseRoom" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 45, 5, 0)
size = Vector3(18, 6, 28)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="EastBaseWindow" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 35, 6, 0)
size = Vector3(2, 4, 16)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="EastBaseRoof" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 45, 16, 0)
size = Vector3(20, 1, 30)
material = SubResource("StandardMaterial3D_grey")
[node name="WestBase" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45, 4, 0)
size = Vector3(20, 8, 30)
material = SubResource("StandardMaterial3D_grey")
[node name="WestBaseRoom" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45, 5, 0)
size = Vector3(18, 6, 28)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="WestBaseWindow" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -35, 6, 0)
size = Vector3(2, 4, 16)
material = SubResource("StandardMaterial3D_grey")
operation = 2
[node name="WestBaseRoof" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45, 16, 0)
size = Vector3(20, 1, 30)
material = SubResource("StandardMaterial3D_grey")
[node name="CoverNE1" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15, 2, -15)
size = Vector3(2, 4, 8)
material = SubResource("StandardMaterial3D_orange")
[node name="CoverNW1" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 2, -15)
size = Vector3(2, 4, 8)
material = SubResource("StandardMaterial3D_orange")
[node name="CoverSE1" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15, 2, 15)
size = Vector3(2, 4, 8)
material = SubResource("StandardMaterial3D_orange")
[node name="CoverSW1" type="CSGBox3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 2, 15)
size = Vector3(2, 4, 8)
material = SubResource("StandardMaterial3D_orange")
[node name="LightCenter" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 15, 0)
light_color = Color(0.2, 0.6, 1.0, 1)
light_energy = 8.0
omni_range = 40.0
shadow_enabled = true
[node name="LightUWW" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -20, 2, 0)
light_color = Color(1.0, 0.4, 0.1, 1)
light_energy = 3.0
omni_range = 20.0
shadow_enabled = true
[node name="LightUWE" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 20, 2, 0)
light_color = Color(1.0, 0.4, 0.1, 1)
light_energy = 3.0
omni_range = 20.0
shadow_enabled = true
[node name="LightTunW" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -30, 3, -45)
light_color = Color(0.1, 0.8, 1.0, 1)
light_energy = 2.0
omni_range = 15.0
shadow_enabled = true
[node name="LightTunE" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 30, 3, -45)
light_color = Color(0.1, 0.8, 1.0, 1)
light_energy = 2.0
omni_range = 15.0
shadow_enabled = true
[node name="LightSpawnE" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 45, 4, 0)
light_color = Color(1.0, 1.0, 1.0, 1)
light_energy = 1.5
omni_range = 12.0
shadow_enabled = true
[node name="LightSpawnW" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -45, 4, 0)
light_color = Color(1.0, 1.0, 1.0, 1)
light_energy = 1.5
omni_range = 12.0
shadow_enabled = true
+5
View File
@@ -0,0 +1,5 @@
[map]
name="FPS Blockout"
scene_path="res://scenes/maps/fps_blockout/fps_blockout.tscn"
color1=Color(0.2, 0.2, 0.2, 1.0)
color2=Color(0.4, 0.45, 0.5, 1.0)
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env -S godot --headless --script
extends SceneTree
func _init():
print("Generating Map programmatically...")
var root = CSGCombiner3D.new()
root.name = "MapRoot"
root.use_collision = true
# 1. Base Geometry
var floor = CSGBox3D.new()
floor.name = "Floor"
floor.size = Vector3(36, 0.5, 36)
floor.position = Vector3(0, -0.25, 0)
root.add_child(floor)
floor.owner = root
# Outer Shell Walls
var top_wall = CSGBox3D.new()
top_wall.name = "TopWall"
top_wall.size = Vector3(36, 3, 0.5)
top_wall.position = Vector3(0, 1.5, -17.75)
root.add_child(top_wall)
top_wall.owner = root
var bot_wall = CSGBox3D.new()
bot_wall.name = "BottomWall"
bot_wall.size = Vector3(36, 3, 0.5)
bot_wall.position = Vector3(0, 1.5, 17.75)
root.add_child(bot_wall)
bot_wall.owner = root
var left_wall = CSGBox3D.new()
left_wall.name = "LeftWall"
left_wall.size = Vector3(0.5, 3, 35)
left_wall.position = Vector3(-17.75, 1.5, 0)
root.add_child(left_wall)
left_wall.owner = root
var right_wall = CSGBox3D.new()
right_wall.name = "RightWall"
right_wall.size = Vector3(0.5, 3, 35)
right_wall.position = Vector3(17.75, 1.5, 0)
root.add_child(right_wall)
right_wall.owner = root
# Inner blocks and subtractions...
_create_room_block(root, "TopLeft", Vector3(-9, 1.5, -9), Vector3(1, 0, 0), Vector3(0, 0, 1))
_create_room_block(root, "TopRight", Vector3(9, 1.5, -9), Vector3(-1, 0, 0), Vector3(0, 0, 1))
_create_room_block(root, "BottomLeft", Vector3(-9, 1.5, 9), Vector3(1, 0, 0), Vector3(0, 0, -1))
_create_room_block(root, "BottomRight", Vector3(9, 1.5, 9), Vector3(-1, 0, 0), Vector3(0, 0, -1))
var packed = PackedScene.new()
var result = packed.pack(root)
if result == OK:
var err = ResourceSaver.save(packed, "res://scenes/maps/fps_blockout.tscn")
if err == OK:
print("Map saved successfully to res://scenes/maps/fps_blockout.tscn")
else:
print("Error saving map: ", err)
else:
print("Error packing scene: ", result)
quit()
func _create_room_block(root: CSGCombiner3D, block_name: String, pos: Vector3, door_horizontal_dir: Vector3, door_vertical_dir: Vector3):
var block = CSGBox3D.new()
block.name = block_name + "Block"
block.size = Vector3(10, 3, 10)
block.position = pos
root.add_child(block)
block.owner = root
var room = CSGBox3D.new()
room.name = block_name + "Room"
room.operation = CSGShape3D.OPERATION_SUBTRACTION
room.size = Vector3(6, 3, 6)
room.position = pos
root.add_child(room)
room.owner = root
var door_h = CSGBox3D.new()
door_h.name = block_name + "DoorH"
door_h.operation = CSGShape3D.OPERATION_SUBTRACTION
# For horizontal doors (East/West), x is the long dimension going through the wall
door_h.size = Vector3(3.0, 3.0, 2.5)
door_h.position = pos + door_horizontal_dir * 3.75
root.add_child(door_h)
door_h.owner = root
var door_v = CSGBox3D.new()
door_v.name = block_name + "DoorV"
door_v.operation = CSGShape3D.OPERATION_SUBTRACTION
# For vertical doors (North/South), z is the long dimension going through the wall
door_v.size = Vector3(2.5, 3.0, 3.0)
door_v.position = pos + door_vertical_dir * 3.75
root.add_child(door_v)
door_v.owner = root
+1
View File
@@ -0,0 +1 @@
uid://rwsrtgh08bhb
+498
View File
@@ -0,0 +1,498 @@
extends Node3D
class_name LevelRuntime
var _speed_label: Label
var _state_label: Label
var _chain_label: Label
var _weapon_label: Label
var _grapple_icon: TextureRect
var _dash_icon: TextureRect
var _grapple_label: Label
var _dash_label: Label
var _fps_label: Label
var _player: CharacterBody3D
var _debug_ui_panel: PanelContainer
func _ready() -> void:
# Hide mouse
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
_build_hud()
# Multiplayer Spawning
var spawner = MultiplayerSpawner.new()
spawner.name = "PlayerSpawner"
spawner.spawn_path = "." # Spawn directly as children of the level
spawner.spawn_function = _spawn_player
add_child(spawner)
var nm = get_node_or_null("/root/NetworkManager")
if nm and nm.multiplayer.is_server():
# Spawn existing players
for pid in nm.player_stats.keys():
spawner.spawn(pid)
# Spawn late joiners
nm.player_connected.connect(func(pid):
spawner.spawn(pid)
)
# Despawn on disconnect
nm.player_disconnected.connect(func(pid):
var p = get_node_or_null(str(pid))
if p:
p.queue_free()
)
# ── Spawning ──────────────────────────────────────────────────────────────────
func _get_dynamic_spawn_position() -> Vector3:
var spawn_nodes = get_tree().get_nodes_in_group("spawn_points")
if spawn_nodes.is_empty():
return Vector3(0, 10, 0)
var all_players = []
for p in get_tree().get_nodes_in_group("players"):
if is_instance_valid(p) and p is Node3D:
all_players.append(p)
if all_players.is_empty():
var pos = spawn_nodes.pick_random().global_position
pos += Vector3(randf_range(-1, 1), 0.5, randf_range(-1, 1))
return pos
var best_spawn = spawn_nodes[0].global_position
var max_min_dist = -1.0
for spawn_node in spawn_nodes:
var pos = spawn_node.global_position
var min_dist_to_player = 999999.0
for p in all_players:
var dist = pos.distance_to(p.global_position)
if dist < min_dist_to_player:
min_dist_to_player = dist
if min_dist_to_player > max_min_dist:
max_min_dist = min_dist_to_player
best_spawn = pos
# Add slight jitter to prevent exact stacking
best_spawn += Vector3(randf_range(-1, 1), 0.5, randf_range(-1, 1))
return best_spawn
func _spawn_player(pid: int) -> CharacterBody3D:
var player := CharacterBody3D.new()
player.name = str(pid)
player.set_multiplayer_authority(pid)
player.add_to_group("players")
# Dynamic Spawning
player.position = _get_dynamic_spawn_position()
# Server Synchronizer (Host is the ground truth)
var server_sync = MultiplayerSynchronizer.new()
server_sync.name = "ServerSynchronizer"
server_sync.set_multiplayer_authority(1) # Host always controls these
var server_rep_config = SceneReplicationConfig.new()
server_rep_config.add_property(":position")
server_rep_config.add_property(":synced_movement_state")
server_rep_config.add_property(":synced_movement_speed")
server_rep_config.add_property(":synced_is_crouching")
server_rep_config.add_property(":health")
server_rep_config.add_property(":shield")
server_rep_config.add_property(":is_dead")
server_rep_config.add_property(":synced_grapple_point")
server_rep_config.add_property(":synced_is_grapple_shooting")
server_sync.replication_config = server_rep_config
player.add_child(server_sync)
# Client Synchronizer (Client dictates their aim and loadout setup)
var client_sync = MultiplayerSynchronizer.new()
client_sync.name = "MultiplayerSynchronizer"
client_sync.set_multiplayer_authority(pid)
var client_rep_config = SceneReplicationConfig.new()
client_rep_config.add_property(":rotation")
client_rep_config.add_property("HeadPivot:rotation")
client_rep_config.add_property(":synced_weapon_path")
client_rep_config.add_property(":synced_loadout_p1")
client_rep_config.add_property(":synced_loadout_p2")
client_rep_config.add_property(":synced_loadout_sp")
client_rep_config.add_property(":synced_loadout_melee")
client_rep_config.add_property(":synced_loadout_ready")
client_rep_config.add_property(":synced_is_targeting")
client_rep_config.add_property(":synced_homing_target_pos")
client_sync.replication_config = client_rep_config
player.add_child(client_sync)
# Collision shape
var col_shape := CollisionShape3D.new()
col_shape.name = "CollisionShape3D"
var capsule := CapsuleShape3D.new()
capsule.radius = 0.4
capsule.height = 1.8
col_shape.shape = capsule
player.add_child(col_shape)
# Apply movement controller script
var mover_script = preload("res://movement/player_movement_controller.gd")
if mover_script:
player.set_script(mover_script)
# Humanoid Model for Player (Shadows only locally, visible to others)
var humanoid = load("res://characters/humanoid_model.gd").new()
humanoid.name = "HumanoidModel"
humanoid.color = Color(0.2, 0.4, 0.8) # Blueish for player
humanoid.shadows_only = (pid == multiplayer.get_unique_id())
humanoid.position = Vector3(0, -0.9, 0) # Offset from center to feet
player.add_child(humanoid)
# Movement State Machine
var sm := Node.new()
sm.name = "MovementStateMachine"
player.add_child(sm)
var sm_script = preload("res://movement/movement_state_machine.gd")
if sm_script:
sm.set_script(sm_script)
# Add all movement states
var state_scripts := {
"ground": "res://movement/states/state_ground.gd",
"air": "res://movement/states/state_air.gd",
"wall_run": "res://movement/states/state_wall_run.gd",
"wall_cling": "res://movement/states/state_wall_cling.gd",
"slide": "res://movement/states/state_slide.gd",
"dash": "res://movement/states/state_dash.gd",
}
for state_name in state_scripts:
var st := Node.new()
st.name = "state_" + state_name
sm.add_child(st)
var st_script = load(state_scripts[state_name])
if st_script:
st.set_script(st_script)
# FPS Camera Rig (HeadPivot → Camera3D)
var head_pivot := Node3D.new()
head_pivot.name = "HeadPivot"
head_pivot.position = Vector3(0, 0.7, 0) # Eye height
player.add_child(head_pivot)
var camera := Camera3D.new()
camera.name = "Camera3D"
camera.current = (pid == multiplayer.get_unique_id())
camera.fov = 90.0
head_pivot.add_child(camera)
# ── Weapon Setup ────────────────────────────────────────────────────────────
var wmanager_script = preload("res://weapons/weapon_manager.gd")
if wmanager_script:
var wman = wmanager_script.new()
wman.name = "WeaponManager"
wman.player = player
wman.camera = camera
camera.add_child(wman)
var rig_script = preload("res://characters/player/fps_camera_rig.gd")
if rig_script:
head_pivot.set_script(rig_script)
# Store original capsule height
sm.original_capsule_height = capsule.height
# ── Dependencies ────────────────────────────────────────────────────────
sm.player = player
sm.params = player.params
head_pivot.params = player.params
# Authority configuration
var is_local = (pid == multiplayer.get_unique_id())
if is_local:
_player = player
player.set_physics_process(true)
player.set_process(true)
sm.set_physics_process(true)
else:
player.set_physics_process(false)
player.set_process(true)
player.set_process_input(false)
sm.set_physics_process(false)
sm.set_process(false)
sm.set_process_input(false)
head_pivot.set_process_input(false)
head_pivot.set_process(false)
head_pivot.set_physics_process(false)
var wman = camera.get_node_or_null("WeaponManager")
if wman:
wman.set_process_input(false)
wman.set_process(false)
wman.set_physics_process(false)
wman.visible = false
var cv = wman.get("canvas_layer")
if cv:
cv.visible = false
return player
# ── HUD ───────────────────────────────────────────────────────────────────────
func _build_hud() -> void:
var canvas := CanvasLayer.new()
canvas.name = "UI"
add_child(canvas)
# ── Crosshair ─────────────────────────────────────────────────────────
var crosshair := Control.new()
crosshair.name = "Crosshair"
crosshair.set_anchors_preset(Control.PRESET_CENTER)
crosshair.custom_minimum_size = Vector2(20, 20)
canvas.add_child(crosshair)
var ch_dot := ColorRect.new()
ch_dot.name = "Dot"
ch_dot.color = Color(1, 1, 1, 0.8)
ch_dot.size = Vector2(4, 4)
ch_dot.position = Vector2(-2, -2)
crosshair.add_child(ch_dot)
# Crosshair lines
for data in [
{"pos": Vector2(-10, -1), "size": Vector2(6, 2)}, # Left
{"pos": Vector2(4, -1), "size": Vector2(6, 2)}, # Right
{"pos": Vector2(-1, -10), "size": Vector2(2, 6)}, # Top
{"pos": Vector2(-1, 4), "size": Vector2(2, 6)}, # Bottom
]:
var line := ColorRect.new()
line.color = Color(1, 1, 1, 0.6)
line.position = data["pos"]
line.size = data["size"]
crosshair.add_child(line)
# ── Info panel background ─────────────────────────────────────────────
_fps_label = Label.new()
_fps_label.name = "FPSLabel"
_fps_label.text = "FPS: 0"
_fps_label.add_theme_font_size_override("font_size", 24)
_fps_label.add_theme_color_override("font_color", Color(0.9, 0.9, 0.2))
_fps_label.add_theme_color_override("font_outline_color", Color(0, 0, 0))
_fps_label.add_theme_constant_override("outline_size", 4)
_fps_label.set_anchors_preset(Control.PRESET_TOP_LEFT)
_fps_label.position = Vector2(12, 12)
canvas.add_child(_fps_label)
_debug_ui_panel = PanelContainer.new()
_debug_ui_panel.name = "InfoPanel"
_debug_ui_panel.offset_left = 12
_debug_ui_panel.offset_top = 50
_debug_ui_panel.offset_right = 400
_debug_ui_panel.offset_bottom = 160
var panel_style := StyleBoxFlat.new()
panel_style.bg_color = Color(0, 0, 0, 0.55)
panel_style.corner_radius_top_left = 8
panel_style.corner_radius_top_right = 8
panel_style.corner_radius_bottom_left = 8
panel_style.corner_radius_bottom_right = 8
panel_style.content_margin_left = 12
panel_style.content_margin_top = 8
panel_style.content_margin_right = 12
panel_style.content_margin_bottom = 8
_debug_ui_panel.add_theme_stylebox_override("panel", panel_style)
canvas.add_child(_debug_ui_panel)
var vbox := VBoxContainer.new()
vbox.name = "InfoVBox"
_debug_ui_panel.add_child(vbox)
_speed_label = Label.new()
_speed_label.name = "SpeedLabel"
_speed_label.text = "Speed: 0.0 m/s"
_speed_label.add_theme_font_size_override("font_size", 18)
_speed_label.add_theme_color_override("font_color", Color(0.3, 1.0, 0.5))
vbox.add_child(_speed_label)
_state_label = Label.new()
_state_label.name = "StateLabel"
_state_label.text = "State: ground"
_state_label.add_theme_font_size_override("font_size", 16)
_state_label.add_theme_color_override("font_color", Color(0.7, 0.85, 1.0))
vbox.add_child(_state_label)
_chain_label = Label.new()
_chain_label.name = "ChainLabel"
_chain_label.text = "Chain: 0 (+0%)"
_chain_label.add_theme_font_size_override("font_size", 16)
_chain_label.add_theme_color_override("font_color", Color(1.0, 0.8, 0.3))
vbox.add_child(_chain_label)
# ── Weapon & Ammo Panel ───────────────────────────────────────────────
var wp_panel := PanelContainer.new()
wp_panel.name = "WeaponPanel"
wp_panel.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
wp_panel.offset_left = -250
wp_panel.offset_top = -100
wp_panel.offset_right = -20
wp_panel.offset_bottom = -20
var wp_style := StyleBoxFlat.new()
wp_style.bg_color = Color(0, 0, 0, 0.6)
wp_style.corner_radius_top_left = 8
wp_style.corner_radius_top_right = 8
wp_style.corner_radius_bottom_left = 8
wp_style.corner_radius_bottom_right = 8
wp_style.content_margin_left = 16
wp_style.content_margin_top = 12
wp_style.content_margin_right = 16
wp_style.content_margin_bottom = 12
wp_panel.add_theme_stylebox_override("panel", wp_style)
wp_panel.grow_horizontal = Control.GROW_DIRECTION_BEGIN
canvas.add_child(wp_panel)
_weapon_label = Label.new()
_weapon_label.name = "WeaponLabel"
_weapon_label.text = "Unarmed\n0 / 0"
_weapon_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
_weapon_label.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
_weapon_label.add_theme_font_size_override("font_size", 24)
wp_panel.add_child(_weapon_label)
# ── Utilities Panel ───────────────────────────────────────────────────────
var util_panel := PanelContainer.new()
util_panel.name = "UtilPanel"
util_panel.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
util_panel.offset_left = -200
util_panel.offset_top = -180
util_panel.offset_right = -20
util_panel.offset_bottom = -110
var util_style := StyleBoxFlat.new()
util_style.bg_color = Color(0, 0, 0, 0.6)
util_style.corner_radius_top_left = 8
util_style.corner_radius_top_right = 8
util_style.corner_radius_bottom_left = 8
util_style.corner_radius_bottom_right = 8
util_style.content_margin_left = 12
util_style.content_margin_top = 8
util_style.content_margin_right = 12
util_style.content_margin_bottom = 8
util_panel.add_theme_stylebox_override("panel", util_style)
util_panel.grow_horizontal = Control.GROW_DIRECTION_BEGIN
canvas.add_child(util_panel)
var util_hbox := HBoxContainer.new()
util_hbox.name = "UtilHBox"
util_hbox.add_theme_constant_override("separation", 20)
util_hbox.alignment = BoxContainer.ALIGNMENT_CENTER
util_panel.add_child(util_hbox)
var grapple_vbox := VBoxContainer.new()
grapple_vbox.alignment = BoxContainer.ALIGNMENT_CENTER
util_hbox.add_child(grapple_vbox)
_grapple_icon = TextureRect.new()
if ResourceLoader.exists("res://assets/ui/grapple_icon.jpg"):
_grapple_icon.texture = load("res://assets/ui/grapple_icon.jpg")
_grapple_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
_grapple_icon.custom_minimum_size = Vector2(32, 32)
_grapple_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
grapple_vbox.add_child(_grapple_icon)
_grapple_label = Label.new()
_grapple_label.text = "Grapple"
_grapple_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_grapple_label.add_theme_font_size_override("font_size", 12)
grapple_vbox.add_child(_grapple_label)
var dash_vbox := VBoxContainer.new()
dash_vbox.alignment = BoxContainer.ALIGNMENT_CENTER
util_hbox.add_child(dash_vbox)
_dash_icon = TextureRect.new()
if ResourceLoader.exists("res://assets/ui/dash_icon.jpg"):
_dash_icon.texture = load("res://assets/ui/dash_icon.jpg")
_dash_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
_dash_icon.custom_minimum_size = Vector2(32, 32)
_dash_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
dash_vbox.add_child(_dash_icon)
_dash_label = Label.new()
_dash_label.text = "Ready"
_dash_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_dash_label.add_theme_font_size_override("font_size", 12)
dash_vbox.add_child(_dash_label)
# ── HUD Update ────────────────────────────────────────────────────────────────
func _process(_delta: float) -> void:
if not _player or not is_instance_valid(_player):
return
if _debug_ui_panel:
_debug_ui_panel.visible = SettingsManager.show_debug_ui
if _fps_label:
_fps_label.visible = SettingsManager.show_fps
if _fps_label.visible:
_fps_label.text = "FPS: %d" % Engine.get_frames_per_second()
var vel: Vector3 = _player.velocity
var hspeed := Vector2(vel.x, vel.z).length()
var total_speed := vel.length()
if _speed_label:
_speed_label.text = "Speed: %.1f m/s (total: %.1f)" % [hspeed, total_speed]
if _state_label:
var sm = _player.get_node_or_null("MovementStateMachine")
if sm:
_state_label.text = "State: %s" % sm.current_state
if _chain_label:
var sm = _player.get_node_or_null("MovementStateMachine")
if sm:
_chain_label.text = "Chain: %d (+%d%%)" % [sm.chain_count, int(sm.current_chain_bonus * 100)]
# Update utility indicators
if sm.current_state == "grapple" or sm.is_grapple_shooting:
_grapple_icon.modulate = Color(0.2, 1.0, 0.4)
_grapple_label.text = "Grappling"
else:
_grapple_icon.modulate = Color(1.0, 1.0, 1.0)
_grapple_label.text = "Ready"
if sm.has_method("get_dash_cooldown_remaining"):
var dash_rem = sm.get_dash_cooldown_remaining()
if dash_rem > 0.0:
_dash_icon.modulate = Color(1.0, 0.3, 0.3)
_dash_label.text = "%.1f" % dash_rem
else:
_dash_icon.modulate = Color(1.0, 1.0, 1.0)
_dash_label.text = "Ready"
if _weapon_label:
var wman = _player.get_node_or_null("HeadPivot/Camera3D/WeaponManager")
if wman and wman.weapons.has(wman.active_slot):
var active_weapon = wman.weapons[wman.active_slot]
var w_name = "Weapon"
var cur_ammo = 0
var max_ammo = 0
if "weapon_name" in active_weapon:
w_name = active_weapon.weapon_name
elif active_weapon is DoubleBarrelShotgun:
w_name = "Double Barrel Shotgun"
if "current_ammo" in active_weapon:
cur_ammo = active_weapon.current_ammo
max_ammo = active_weapon.max_ammo
elif "shells" in active_weapon:
cur_ammo = active_weapon.shells
max_ammo = 2
if "reloading" in active_weapon and active_weapon.reloading:
_weapon_label.text = "%s\nReloading..." % w_name
else:
_weapon_label.text = "%s\n%d / %d" % [w_name, cur_ammo, max_ammo]
else:
_weapon_label.text = "Unarmed\n0 / 0"
+1
View File
@@ -0,0 +1 @@
uid://bv0ov3bo3p0uu
@@ -0,0 +1,221 @@
@tool
extends Node3D
class_name ArenaGenerator
@export var generate: bool = false:
set(value):
if value:
if not is_inside_tree():
call_deferred("_generate_tactical_arena")
else:
_generate_tactical_arena()
generate = false
@export var clear: bool = false:
set(value):
if value:
_clear_arena()
clear = false
@export var sector_size = 24.0
@export var step_height = 3.0
@export var wall_height = 12.0
@export var floor_color = Color(0.2, 0.22, 0.25)
@export var wall_color = Color(0.3, 0.4, 0.5)
@export var obstacle_color = Color(0.6, 0.35, 0.15)
@export var ramp_color = Color(0.7, 0.4, 0.2)
func _clear_arena() -> void:
var children = get_children()
for c in children:
remove_child(c)
c.free()
func _generate_tactical_arena() -> void:
_clear_arena()
var cols = 3 # Left, Mid, Right
var rows = 5 # Spawn A, Conn A, Mid, Conn B, Spawn B
# Generate elevations (symmetric for fairness)
var elevations = []
for x in range(cols):
elevations.append([])
for z in range(rows):
elevations[x].append(0)
# Randomize half, mirror other half
for x in range(cols):
for z in range(3): # 0, 1, 2
elevations[x][z] = randi_range(0, 2)
# Mirror to the other side (point symmetry)
# Or mirror horizontally (Spawn A == Spawn B setup)
# A standard mirror across mid:
for x in range(cols):
elevations[x][3] = elevations[cols - 1 - x][1]
elevations[x][4] = elevations[cols - 1 - x][0]
# Build Sector Floors
var _sec_size = float(sector_size if sector_size != null else 24.0)
var _step_h = float(step_height if step_height != null else 3.0)
var _wall_h = float(wall_height if wall_height != null else 12.0)
var _f_col = floor_color if floor_color != null else Color(0.2, 0.22, 0.25)
var _w_col = wall_color if wall_color != null else Color(0.3, 0.4, 0.5)
var hw = (cols * _sec_size) / 2.0
var hl = (rows * _sec_size) / 2.0
# Global origin is center of grid
for x in range(cols):
for z in range(rows):
var cx = -hw + x * _sec_size + _sec_size / 2.0
var cz = -hl + z * _sec_size + _sec_size / 2.0
var y = float(elevations[x][z]) * _step_h
# Make floors incredibly thick (40 units) so they act as solid pillars all the way down
# This prevents players from walking underneath higher sectors and falling into the void
_create_box("Sector_%d_%d" % [x, z], Vector3(cx, y - 20.0, cz), Vector3(_sec_size, 40.0, _sec_size), _f_col)
# Add cover in sector (except spawns z=0 and z=4)
if z > 0 and z < 4:
_generate_cover("Cover_%d_%d" % [x, z], Vector3(cx, y, cz))
# Ramps between Z adjacent (North/South)
if z < rows - 1:
var diff = elevations[x][z+1] - elevations[x][z]
if diff != 0:
_create_ramp_z("RampZ_%d_%d" % [x, z], cx, cz + _sec_size / 2.0, y, diff)
# Ramps/Walls between X adjacent (East/West)
if x < cols - 1:
var is_connector = randf() > 0.4 # 60% chance to be an open connector
var diff = elevations[x+1][z] - elevations[x][z]
if is_connector:
if diff != 0:
_create_ramp_x("RampX_%d_%d" % [x, z], cx + _sec_size / 2.0, cz, y, diff)
else:
# Solid wall
var max_y = max(elevations[x][z], elevations[x+1][z]) * _step_h
_create_box("InnerWall_%d_%d" % [x, z], Vector3(cx + _sec_size / 2.0, max_y + _wall_h/2, cz), Vector3(1.0, _wall_h, _sec_size), _w_col)
# Outer Walls
_create_box("Wall_North", Vector3(0, _wall_h * 0.5, -hl), Vector3(hw * 2, _wall_h, 1.0), _w_col)
_create_box("Wall_South", Vector3(0, _wall_h * 0.5, hl), Vector3(hw * 2, _wall_h, 1.0), _w_col)
_create_box("Wall_East", Vector3(hw, _wall_h * 0.5, 0), Vector3(1.0, _wall_h, hl * 2), _w_col)
_create_box("Wall_West", Vector3(-hw, _wall_h * 0.5, 0), Vector3(1.0, _wall_h, hl * 2), _w_col)
func _generate_cover(base_name: String, center: Vector3) -> void:
var _sec_size = float(sector_size if sector_size != null else 24.0)
var _o_col = obstacle_color if obstacle_color != null else Color(0.6, 0.35, 0.15)
# Generate 1-3 blocks of cover
var count = randi_range(1, 3)
for i in range(count):
var ox = randf_range(-_sec_size*0.3, _sec_size*0.3)
var oz = randf_range(-_sec_size*0.3, _sec_size*0.3)
var sx = randf_range(2.0, 6.0)
var sy = randf_range(1.0, 4.0)
var sz = randf_range(2.0, 6.0)
_create_box("%s_%d" % [base_name, i], center + Vector3(ox, sy * 0.5, oz), Vector3(sx, sy, sz), _o_col)
func _create_ramp_z(node_name: String, cx: float, cz: float, base_y: float, diff_levels: int) -> void:
var _step_h = float(step_height if step_height != null else 3.0)
var _sec_size = float(sector_size if sector_size != null else 24.0)
var _r_col = ramp_color if ramp_color != null else Color(0.7, 0.4, 0.2)
# Ramp along Z axis (North/South)
var h = abs(diff_levels) * _step_h
var length = _sec_size * 0.4 # Ramp takes up 40% of sector depth
var center_y = base_y + h / 2.0
var dir = sign(diff_levels)
var rot_x = 0
if dir > 0:
rot_x = rad_to_deg(atan2(h, length))
else:
rot_x = -rad_to_deg(atan2(h, length))
var true_length = sqrt(length*length + h*h)
_create_ramp(node_name, Vector3(cx, center_y, cz), Vector3(_sec_size * 0.5, 0.5, true_length), Vector3(rot_x, 0, 0), _r_col)
func _create_ramp_x(node_name: String, cx: float, cz: float, base_y: float, diff_levels: int) -> void:
var _step_h = float(step_height if step_height != null else 3.0)
var _sec_size = float(sector_size if sector_size != null else 24.0)
var _r_col = ramp_color if ramp_color != null else Color(0.7, 0.4, 0.2)
# Ramp along X axis (East/West)
var h = abs(diff_levels) * _step_h
var length = _sec_size * 0.4
var center_y = base_y + h / 2.0
var dir = sign(diff_levels)
var rot_z = 0
if dir > 0:
rot_z = -rad_to_deg(atan2(h, length))
else:
rot_z = rad_to_deg(atan2(h, length))
var true_length = sqrt(length*length + h*h)
_create_ramp(node_name, Vector3(cx, center_y, cz), Vector3(true_length, 0.5, _sec_size * 0.5), Vector3(0, 0, rot_z), _r_col)
func _create_box(node_name: String, pos: Vector3, size: Vector3, color: Color) -> void:
var body := StaticBody3D.new()
body.name = node_name
body.position = pos
var shape := CollisionShape3D.new()
shape.name = "CollisionShape3D"
shape.shape = BoxShape3D.new()
shape.shape.size = size
body.add_child(shape)
var mesh := MeshInstance3D.new()
mesh.name = "MeshInstance3D"
mesh.mesh = BoxMesh.new()
mesh.mesh.size = size
var mat := StandardMaterial3D.new()
mat.albedo_color = color
mat.roughness = 0.8
mesh.mesh.surface_set_material(0, mat)
body.add_child(mesh)
add_child(body)
if Engine.is_editor_hint():
var scene_root = get_tree().edited_scene_root
if scene_root:
body.owner = scene_root
shape.owner = scene_root
mesh.owner = scene_root
func _create_ramp(node_name: String, pos: Vector3, size: Vector3, rot_deg: Vector3, color: Color) -> void:
var body := StaticBody3D.new()
body.name = node_name
body.position = pos
body.rotation_degrees = rot_deg
var shape := CollisionShape3D.new()
shape.name = "CollisionShape3D"
shape.shape = BoxShape3D.new()
shape.shape.size = size
body.add_child(shape)
var mesh := MeshInstance3D.new()
mesh.name = "MeshInstance3D"
mesh.mesh = BoxMesh.new()
mesh.mesh.size = size
var mat := StandardMaterial3D.new()
mat.albedo_color = color
mat.roughness = 0.8
mesh.mesh.surface_set_material(0, mat)
body.add_child(mesh)
add_child(body)
if Engine.is_editor_hint():
var scene_root = get_tree().edited_scene_root
if scene_root:
body.owner = scene_root
shape.owner = scene_root
mesh.owner = scene_root
@@ -0,0 +1 @@
uid://cxf17ncr1fxd1
@@ -0,0 +1,5 @@
[map]
name="Procedural Arena"
scene_path="res://scenes/maps/procedural_arena/procedural_arena.tscn"
color1=Color(0.15, 0.4, 0.2, 1.0)
color2=Color(0.2, 0.6, 0.3, 1.0)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,477 @@
extends Node3D
var _speed_label: Label
var _state_label: Label
var _chain_label: Label
var _weapon_label: Label
var _grapple_icon: TextureRect
var _dash_icon: TextureRect
var _grapple_label: Label
var _dash_label: Label
var _fps_label: Label
var _player: CharacterBody3D
var _debug_ui_panel: PanelContainer
func _ready() -> void:
# Hide mouse
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
_build_hud()
# Multiplayer Spawning
var spawner = MultiplayerSpawner.new()
spawner.name = "PlayerSpawner"
spawner.spawn_path = "." # Spawn directly as children of the level
spawner.spawn_function = _spawn_player
add_child(spawner)
var nm = get_node_or_null("/root/NetworkManager")
if nm and nm.multiplayer.is_server():
# Spawn existing players
for pid in nm.player_stats.keys():
spawner.spawn(pid)
# Spawn late joiners
nm.player_connected.connect(func(pid):
spawner.spawn(pid)
)
# Despawn on disconnect
nm.player_disconnected.connect(func(pid):
var p = get_node_or_null(str(pid))
if p:
p.queue_free()
)
# ── Player ────────────────────────────────────────────────────────────────────
func _spawn_player(pid: int) -> CharacterBody3D:
var player := CharacterBody3D.new()
player.name = str(pid)
player.set_multiplayer_authority(pid)
# Alternate spawn sides (Team A / Team B style)
var spawn_z = 40.0 if (pid % 2 == 0) else -40.0
player.position = Vector3(randf_range(-6, 6), 10.0, spawn_z + randf_range(-4, 4))
# Server Synchronizer (Host is the ground truth)
var server_sync = MultiplayerSynchronizer.new()
server_sync.name = "ServerSynchronizer"
server_sync.set_multiplayer_authority(1) # Host always controls these
var server_rep_config = SceneReplicationConfig.new()
server_rep_config.add_property(":position")
server_rep_config.add_property(":synced_movement_state")
server_rep_config.add_property(":synced_movement_speed")
server_rep_config.add_property(":synced_is_crouching")
server_rep_config.add_property(":health")
server_rep_config.add_property(":shield")
server_rep_config.add_property(":is_dead")
server_rep_config.add_property(":synced_grapple_point")
server_rep_config.add_property(":synced_is_grapple_shooting")
server_sync.replication_config = server_rep_config
player.add_child(server_sync)
# Client Synchronizer (Client dictates their aim and loadout setup)
var client_sync = MultiplayerSynchronizer.new()
client_sync.name = "MultiplayerSynchronizer"
client_sync.set_multiplayer_authority(pid)
var client_rep_config = SceneReplicationConfig.new()
client_rep_config.add_property(":rotation")
client_rep_config.add_property("HeadPivot:rotation")
client_rep_config.add_property(":synced_weapon_path")
client_rep_config.add_property(":synced_loadout_p1")
client_rep_config.add_property(":synced_loadout_p2")
client_rep_config.add_property(":synced_loadout_sp")
client_rep_config.add_property(":synced_loadout_melee")
client_rep_config.add_property(":synced_loadout_ready")
client_rep_config.add_property(":synced_is_targeting")
client_rep_config.add_property(":synced_homing_target_pos")
client_sync.replication_config = client_rep_config
player.add_child(client_sync)
# Collision shape
var col_shape := CollisionShape3D.new()
col_shape.name = "CollisionShape3D"
var capsule := CapsuleShape3D.new()
capsule.radius = 0.4
capsule.height = 1.8
col_shape.shape = capsule
player.add_child(col_shape)
# Apply movement controller script
var mover_script = preload("res://movement/player_movement_controller.gd")
if mover_script:
player.set_script(mover_script)
# Humanoid Model for Player (Shadows only locally, visible to others)
var humanoid = load("res://characters/humanoid_model.gd").new()
humanoid.name = "HumanoidModel"
humanoid.color = Color(0.2, 0.4, 0.8) # Blueish for player
humanoid.shadows_only = (pid == multiplayer.get_unique_id())
humanoid.position = Vector3(0, -0.9, 0) # Offset from center to feet
player.add_child(humanoid)
# Movement State Machine
var sm := Node.new()
sm.name = "MovementStateMachine"
player.add_child(sm)
var sm_script = preload("res://movement/movement_state_machine.gd")
if sm_script:
sm.set_script(sm_script)
# Add all movement states
var state_scripts := {
"ground": "res://movement/states/state_ground.gd",
"air": "res://movement/states/state_air.gd",
"wall_run": "res://movement/states/state_wall_run.gd",
"wall_cling": "res://movement/states/state_wall_cling.gd",
"slide": "res://movement/states/state_slide.gd",
"dash": "res://movement/states/state_dash.gd",
}
for state_name in state_scripts:
var st := Node.new()
st.name = "state_" + state_name
sm.add_child(st)
var st_script = load(state_scripts[state_name])
if st_script:
st.set_script(st_script)
# FPS Camera Rig (HeadPivot → Camera3D)
var head_pivot := Node3D.new()
head_pivot.name = "HeadPivot"
head_pivot.position = Vector3(0, 0.7, 0) # Eye height
player.add_child(head_pivot)
var camera := Camera3D.new()
camera.name = "Camera3D"
camera.current = (pid == multiplayer.get_unique_id())
camera.fov = 90.0
head_pivot.add_child(camera)
# ── Weapon Setup ────────────────────────────────────────────────────────────
var wmanager_script = preload("res://weapons/weapon_manager.gd")
if wmanager_script:
var wman = wmanager_script.new()
wman.name = "WeaponManager"
wman.player = player
wman.camera = camera
camera.add_child(wman)
var rig_script = preload("res://characters/player/fps_camera_rig.gd")
if rig_script:
head_pivot.set_script(rig_script)
# Store original capsule height
sm.original_capsule_height = capsule.height
# ── Dependencies ────────────────────────────────────────────────────────
sm.player = player
sm.params = player.params
head_pivot.params = player.params
# Authority configuration
var is_local = (pid == multiplayer.get_unique_id())
if is_local:
_player = player
player.set_physics_process(true)
player.set_process(true)
sm.set_physics_process(true)
else:
player.set_physics_process(false)
player.set_process(true)
player.set_process_input(false)
sm.set_physics_process(false)
sm.set_process(false)
sm.set_process_input(false)
head_pivot.set_process_input(false)
head_pivot.set_process(false)
head_pivot.set_physics_process(false)
var wman = camera.get_node_or_null("WeaponManager")
if wman:
wman.set_process_input(false)
wman.set_process(false)
wman.set_physics_process(false)
wman.visible = false
var cv = wman.get("canvas_layer")
if cv:
cv.visible = false
return player
# ── HUD ───────────────────────────────────────────────────────────────────────
func _build_hud() -> void:
var canvas := CanvasLayer.new()
canvas.name = "UI"
add_child(canvas)
# ── Crosshair ─────────────────────────────────────────────────────────
var crosshair := Control.new()
crosshair.name = "Crosshair"
crosshair.set_anchors_preset(Control.PRESET_CENTER)
crosshair.custom_minimum_size = Vector2(20, 20)
canvas.add_child(crosshair)
var ch_dot := ColorRect.new()
ch_dot.name = "Dot"
ch_dot.color = Color(1, 1, 1, 0.8)
ch_dot.size = Vector2(4, 4)
ch_dot.position = Vector2(-2, -2)
crosshair.add_child(ch_dot)
# Crosshair lines
for data in [
{"pos": Vector2(-10, -1), "size": Vector2(6, 2)}, # Left
{"pos": Vector2(4, -1), "size": Vector2(6, 2)}, # Right
{"pos": Vector2(-1, -10), "size": Vector2(2, 6)}, # Top
{"pos": Vector2(-1, 4), "size": Vector2(2, 6)}, # Bottom
]:
var line := ColorRect.new()
line.color = Color(1, 1, 1, 0.6)
line.position = data["pos"]
line.size = data["size"]
crosshair.add_child(line)
# ── Info panel background ─────────────────────────────────────────────
_fps_label = Label.new()
_fps_label.name = "FPSLabel"
_fps_label.text = "FPS: 0"
_fps_label.add_theme_font_size_override("font_size", 24)
_fps_label.add_theme_color_override("font_color", Color(0.9, 0.9, 0.2))
_fps_label.add_theme_color_override("font_outline_color", Color(0, 0, 0))
_fps_label.add_theme_constant_override("outline_size", 4)
_fps_label.set_anchors_preset(Control.PRESET_TOP_LEFT)
_fps_label.position = Vector2(12, 12)
canvas.add_child(_fps_label)
_debug_ui_panel = PanelContainer.new()
_debug_ui_panel.name = "InfoPanel"
_debug_ui_panel.offset_left = 12
_debug_ui_panel.offset_top = 50
_debug_ui_panel.offset_right = 400
_debug_ui_panel.offset_bottom = 160
var panel_style := StyleBoxFlat.new()
panel_style.bg_color = Color(0, 0, 0, 0.55)
panel_style.corner_radius_top_left = 8
panel_style.corner_radius_top_right = 8
panel_style.corner_radius_bottom_left = 8
panel_style.corner_radius_bottom_right = 8
panel_style.content_margin_left = 12
panel_style.content_margin_top = 8
panel_style.content_margin_right = 12
panel_style.content_margin_bottom = 8
_debug_ui_panel.add_theme_stylebox_override("panel", panel_style)
canvas.add_child(_debug_ui_panel)
var vbox := VBoxContainer.new()
vbox.name = "InfoVBox"
_debug_ui_panel.add_child(vbox)
_speed_label = Label.new()
_speed_label.name = "SpeedLabel"
_speed_label.text = "Speed: 0.0 m/s"
_speed_label.add_theme_font_size_override("font_size", 18)
_speed_label.add_theme_color_override("font_color", Color(0.3, 1.0, 0.5))
vbox.add_child(_speed_label)
_state_label = Label.new()
_state_label.name = "StateLabel"
_state_label.text = "State: ground"
_state_label.add_theme_font_size_override("font_size", 16)
_state_label.add_theme_color_override("font_color", Color(0.7, 0.85, 1.0))
vbox.add_child(_state_label)
_chain_label = Label.new()
_chain_label.name = "ChainLabel"
_chain_label.text = "Chain: 0 (+0%)"
_chain_label.add_theme_font_size_override("font_size", 16)
_chain_label.add_theme_color_override("font_color", Color(1.0, 0.8, 0.3))
vbox.add_child(_chain_label)
# ── Weapon & Ammo Panel ───────────────────────────────────────────────
var wp_panel := PanelContainer.new()
wp_panel.name = "WeaponPanel"
wp_panel.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
wp_panel.offset_left = -250
wp_panel.offset_top = -100
wp_panel.offset_right = -20
wp_panel.offset_bottom = -20
var wp_style := StyleBoxFlat.new()
wp_style.bg_color = Color(0, 0, 0, 0.6)
wp_style.corner_radius_top_left = 8
wp_style.corner_radius_top_right = 8
wp_style.corner_radius_bottom_left = 8
wp_style.corner_radius_bottom_right = 8
wp_style.content_margin_left = 16
wp_style.content_margin_top = 12
wp_style.content_margin_right = 16
wp_style.content_margin_bottom = 12
wp_panel.add_theme_stylebox_override("panel", wp_style)
wp_panel.grow_horizontal = Control.GROW_DIRECTION_BEGIN
canvas.add_child(wp_panel)
_weapon_label = Label.new()
_weapon_label.name = "WeaponLabel"
_weapon_label.text = "Unarmed\n0 / 0"
_weapon_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
_weapon_label.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
_weapon_label.add_theme_font_size_override("font_size", 24)
wp_panel.add_child(_weapon_label)
# ── Utilities Panel ───────────────────────────────────────────────────────
var util_panel := PanelContainer.new()
util_panel.name = "UtilPanel"
util_panel.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
util_panel.offset_left = -200
util_panel.offset_top = -180
util_panel.offset_right = -20
util_panel.offset_bottom = -110
var util_style := StyleBoxFlat.new()
util_style.bg_color = Color(0, 0, 0, 0.6)
util_style.corner_radius_top_left = 8
util_style.corner_radius_top_right = 8
util_style.corner_radius_bottom_left = 8
util_style.corner_radius_bottom_right = 8
util_style.content_margin_left = 12
util_style.content_margin_top = 8
util_style.content_margin_right = 12
util_style.content_margin_bottom = 8
util_panel.add_theme_stylebox_override("panel", util_style)
util_panel.grow_horizontal = Control.GROW_DIRECTION_BEGIN
canvas.add_child(util_panel)
var util_hbox := HBoxContainer.new()
util_hbox.name = "UtilHBox"
util_hbox.add_theme_constant_override("separation", 20)
util_hbox.alignment = BoxContainer.ALIGNMENT_CENTER
util_panel.add_child(util_hbox)
var grapple_vbox := VBoxContainer.new()
grapple_vbox.alignment = BoxContainer.ALIGNMENT_CENTER
util_hbox.add_child(grapple_vbox)
_grapple_icon = TextureRect.new()
_grapple_icon.texture = load("res://assets/ui/grapple_icon.jpg")
_grapple_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
_grapple_icon.custom_minimum_size = Vector2(32, 32)
_grapple_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
grapple_vbox.add_child(_grapple_icon)
_grapple_label = Label.new()
_grapple_label.text = "Grapple"
_grapple_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_grapple_label.add_theme_font_size_override("font_size", 12)
grapple_vbox.add_child(_grapple_label)
var dash_vbox := VBoxContainer.new()
dash_vbox.alignment = BoxContainer.ALIGNMENT_CENTER
util_hbox.add_child(dash_vbox)
_dash_icon = TextureRect.new()
_dash_icon.texture = load("res://assets/ui/dash_icon.jpg")
_dash_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
_dash_icon.custom_minimum_size = Vector2(32, 32)
_dash_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
dash_vbox.add_child(_dash_icon)
_dash_label = Label.new()
_dash_label.text = "Ready"
_dash_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_dash_label.add_theme_font_size_override("font_size", 12)
dash_vbox.add_child(_dash_label)
# ── Utility: Key Name ─────────────────────────────────────────────────────────
func _get_key_name(action: String) -> String:
if not InputMap.has_action(action):
return "?"
var events = InputMap.action_get_events(action)
for e in events:
if e is InputEventKey:
var code = e.physical_keycode if e.physical_keycode != 0 else e.keycode
return OS.get_keycode_string(code)
elif e is InputEventMouseButton:
if e.button_index == MOUSE_BUTTON_LEFT: return "LClick"
elif e.button_index == MOUSE_BUTTON_RIGHT: return "RClick"
elif e.button_index == MOUSE_BUTTON_MIDDLE: return "MClick"
return "?"
# ── HUD Update ────────────────────────────────────────────────────────────────
func _process(_delta: float) -> void:
if not _player or not is_instance_valid(_player):
return
if _debug_ui_panel:
_debug_ui_panel.visible = SettingsManager.show_debug_ui
if _fps_label:
_fps_label.visible = SettingsManager.show_fps
if _fps_label.visible:
_fps_label.text = "FPS: %d" % Engine.get_frames_per_second()
var vel: Vector3 = _player.velocity
var hspeed := Vector2(vel.x, vel.z).length()
var total_speed := vel.length()
if _speed_label:
_speed_label.text = "Speed: %.1f m/s (total: %.1f)" % [hspeed, total_speed]
if _state_label:
var sm = _player.get_node_or_null("MovementStateMachine")
if sm:
_state_label.text = "State: %s" % sm.current_state
if _chain_label:
var sm = _player.get_node_or_null("MovementStateMachine")
if sm:
_chain_label.text = "Chain: %d (+%d%%)" % [sm.chain_count, int(sm.current_chain_bonus * 100)]
# Update utility indicators
if sm.current_state == "grapple" or sm.is_grapple_shooting:
_grapple_icon.modulate = Color(0.2, 1.0, 0.4)
_grapple_label.text = "Grappling"
else:
_grapple_icon.modulate = Color(1.0, 1.0, 1.0)
_grapple_label.text = "Ready"
var dash_rem = sm.get_dash_cooldown_remaining()
if dash_rem > 0.0:
_dash_icon.modulate = Color(1.0, 0.3, 0.3)
_dash_label.text = "%.1f" % dash_rem
else:
_dash_icon.modulate = Color(1.0, 1.0, 1.0)
_dash_label.text = "Ready"
if _weapon_label:
var wman = _player.get_node_or_null("HeadPivot/Camera3D/WeaponManager")
if wman and wman.weapons.has(wman.active_slot):
var active_weapon = wman.weapons[wman.active_slot]
var w_name = "Weapon"
var cur_ammo = 0
var max_ammo = 0
if "weapon_name" in active_weapon:
w_name = active_weapon.weapon_name
elif active_weapon is DoubleBarrelShotgun:
w_name = "Double Barrel Shotgun"
if "current_ammo" in active_weapon:
cur_ammo = active_weapon.current_ammo
max_ammo = active_weapon.max_ammo
elif "shells" in active_weapon:
cur_ammo = active_weapon.shells
max_ammo = 2
if "reloading" in active_weapon and active_weapon.reloading:
_weapon_label.text = "%s\nReloading..." % w_name
else:
_weapon_label.text = "%s\n%d / %d" % [w_name, cur_ammo, max_ammo]
else:
_weapon_label.text = "Unarmed\n0 / 0"
@@ -0,0 +1 @@
uid://1oenqnfde4yw
+5
View File
@@ -0,0 +1,5 @@
[map]
name="Test Level"
scene_path="res://scenes/maps/test_level/test_level.tscn"
color1=Color(0.2, 0.2, 0.25, 1.0)
color2=Color(0.3, 0.5, 0.6, 1.0)
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
[gd_scene format=3 uid="uid://pp1g1inidixa"]
[ext_resource type="Script" uid="uid://c7ltcn37gfd71" path="res://debug/test_level_builder.gd" id="1_2venv"]
[node name="TestLevel" type="Node3D" unique_id=130984349]
script = ExtResource("1_2venv")
+29 -13
View File
@@ -87,17 +87,7 @@ func _ready() -> void:
cards_hbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
_level_selector_panel.add_child(cards_hbox)
# Level 1: Dust 2
var d2_gradient = Gradient.new()
d2_gradient.add_point(0.0, Color(0.9, 0.8, 0.6)) # Sand
d2_gradient.add_point(1.0, Color(0.6, 0.5, 0.3)) # Dark sand
_build_level_card(cards_hbox, "Dust 2", "res://scenes/dust2_level/dust2_level.tscn", d2_gradient)
# Level 2: Test Level
var tl_gradient = Gradient.new()
tl_gradient.add_point(0.0, Color(0.2, 0.2, 0.25)) # Dark grey
tl_gradient.add_point(1.0, Color(0.3, 0.5, 0.6)) # Steel blue
_build_level_card(cards_hbox, "Test Level", "res://scenes/test_level/test_level.tscn", tl_gradient)
_load_maps_modularly(cards_hbox, false, Vector2(300, 400))
var ls_spacer = Control.new()
ls_spacer.custom_minimum_size = Vector2(0, 20)
@@ -224,8 +214,7 @@ func _ready() -> void:
hl_cards.add_theme_constant_override("separation", 20)
hl_left.add_child(hl_cards)
_build_level_card(hl_cards, "Dust 2", "res://scenes/dust2_level/dust2_level.tscn", d2_gradient, true, Vector2(200, 250))
_build_level_card(hl_cards, "Test Level", "res://scenes/test_level/test_level.tscn", tl_gradient, true, Vector2(200, 250))
_load_maps_modularly(hl_cards, true, Vector2(200, 250))
var mode_label = Label.new()
mode_label.text = "Gamemode"
@@ -277,6 +266,33 @@ func _ready() -> void:
_host_lobby_players_list.add_theme_font_size_override("font_size", 24)
hl_right.add_child(_host_lobby_players_list)
func _load_maps_modularly(target_hbox: Container, is_multiplayer: bool, card_size: Vector2) -> void:
var maps_dir = "res://scenes/maps/"
var dir = DirAccess.open(maps_dir)
if not dir:
return
dir.list_dir_begin()
var folder_name = dir.get_next()
while folder_name != "":
if dir.current_is_dir() and not folder_name.begins_with("."):
var cfg_path = maps_dir + folder_name + "/map_meta.cfg"
if FileAccess.file_exists(cfg_path):
var cfg = ConfigFile.new()
if cfg.load(cfg_path) == OK:
var map_name = cfg.get_value("map", "name", "Unknown Map")
var scene_path = cfg.get_value("map", "scene_path", "")
var c1 = cfg.get_value("map", "color1", Color(0.5, 0.5, 0.5))
var c2 = cfg.get_value("map", "color2", Color(0.2, 0.2, 0.2))
if scene_path != "":
var grad = Gradient.new()
# A new Gradient has 2 points at 0.0 and 1.0 by default. Set their colors directly.
grad.set_color(0, c1)
grad.set_color(1, c2)
_build_level_card(target_hbox, map_name, scene_path, grad, is_multiplayer, card_size)
folder_name = dir.get_next()
func _build_level_card(parent: Container, title: String, scene_path: String, gradient: Gradient, is_multiplayer: bool = false, card_size: Vector2 = Vector2(300, 400)) -> void:
var btn = TextureButton.new()
btn.custom_minimum_size = card_size
+45 -1
View File
@@ -25,6 +25,8 @@ var editing_index: int = -1
# Settings UI
var settings_editor: Control
var settings_back_btn: Button
var _world_fov_slider: HSlider
var _world_fov_input: LineEdit
var _debug_ui_checkbox: CheckButton
var _mouse_sens_slider: HSlider
var _mouse_sens_input: LineEdit
@@ -279,6 +281,43 @@ func _build_ui() -> void:
)
user_hbox.add_child(user_save)
var fov_hbox = HBoxContainer.new()
general_tab.add_child(fov_hbox)
var fov_lbl = Label.new()
fov_lbl.text = "World FOV"
fov_lbl.custom_minimum_size = Vector2(150, 0)
fov_hbox.add_child(fov_lbl)
_world_fov_slider = HSlider.new()
_world_fov_slider.min_value = 70.0
_world_fov_slider.max_value = 120.0
_world_fov_slider.step = 1.0
_world_fov_slider.value = SettingsManager.world_fov
_world_fov_slider.custom_minimum_size = Vector2(200, 0)
_world_fov_slider.size_flags_vertical = Control.SIZE_SHRINK_CENTER
fov_hbox.add_child(_world_fov_slider)
_world_fov_input = LineEdit.new()
_world_fov_input.text = str(round(SettingsManager.world_fov))
_world_fov_input.custom_minimum_size = Vector2(60, 0)
fov_hbox.add_child(_world_fov_input)
_world_fov_slider.value_changed.connect(func(value: float):
_world_fov_input.text = str(round(value))
SettingsManager.world_fov = value
SettingsManager._save_settings()
)
_world_fov_input.text_submitted.connect(func(new_text: String):
var val = new_text.to_float()
val = clampf(val, 70.0, 120.0)
_world_fov_slider.value = val
_world_fov_input.text = str(round(val))
SettingsManager.world_fov = val
SettingsManager._save_settings()
)
var spacer_gen = Control.new()
spacer_gen.size_flags_vertical = Control.SIZE_EXPAND_FILL
general_tab.add_child(spacer_gen)
@@ -286,7 +325,9 @@ func _build_ui() -> void:
var reset_gen_btn = Button.new()
reset_gen_btn.text = "Reset to Defaults"
reset_gen_btn.pressed.connect(func():
pass # No settings yet
_world_fov_slider.value = 90.0
SettingsManager.world_fov = 90.0
SettingsManager._save_settings()
)
general_tab.add_child(reset_gen_btn)
@@ -743,6 +784,9 @@ func _show_settings() -> void:
_debug_ui_checkbox.button_pressed = SettingsManager.show_debug_ui
if _show_fps_checkbox:
_show_fps_checkbox.button_pressed = SettingsManager.show_fps
if _world_fov_slider:
_world_fov_slider.value = SettingsManager.world_fov
_world_fov_input.text = str(round(SettingsManager.world_fov))
if _mouse_sens_slider:
var display_val = round(SettingsManager.mouse_sensitivity * 10000.0)
_mouse_sens_slider.value = display_val
+9 -45
View File
@@ -14,52 +14,16 @@ func _init() -> void:
spread_angle = 0.015
func _build_model() -> void:
# Position
position = Vector3(0.3, -0.3, -0.6)
# Receiver
var receiver = MeshInstance3D.new()
var rec_mesh = BoxMesh.new()
rec_mesh.size = Vector3(0.06, 0.12, 0.3)
var rec_mat = StandardMaterial3D.new()
rec_mat.albedo_color = Color(0.2, 0.2, 0.2)
rec_mesh.material = rec_mat
receiver.mesh = rec_mesh
receiver.position = Vector3(0, 0, 0.1)
model_root.add_child(receiver)
# Barrel
var barrel = MeshInstance3D.new()
var b_mesh = CylinderMesh.new()
b_mesh.top_radius = 0.015
b_mesh.bottom_radius = 0.015
b_mesh.height = 0.45
b_mesh.material = rec_mat
barrel.mesh = b_mesh
barrel.rotation.x = deg_to_rad(90)
barrel.position = Vector3(0, 0.03, -0.2)
model_root.add_child(barrel)
# Wood Handguard
var guard = MeshInstance3D.new()
var g_mesh = BoxMesh.new()
g_mesh.size = Vector3(0.08, 0.08, 0.2)
var wood_mat = StandardMaterial3D.new()
wood_mat.albedo_color = Color(0.5, 0.25, 0.1)
g_mesh.material = wood_mat
guard.mesh = g_mesh
guard.position = Vector3(0, 0, -0.1)
model_root.add_child(guard)
# Wood Stock
var stock = MeshInstance3D.new()
var s_mesh = BoxMesh.new()
s_mesh.size = Vector3(0.06, 0.1, 0.25)
s_mesh.material = wood_mat
stock.mesh = s_mesh
stock.position = Vector3(0, -0.02, 0.35)
model_root.add_child(stock)
position = Vector3(0.3, -0.3, -0.590)
var model_scene = load("res://assets/weapons/ak47/source/scene.gltf")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.00125, 0.00125, 0.00125)
model.rotation_degrees.y = 90
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Muzzle Flash
muzzle_flash = OmniLight3D.new()
muzzle_flash.light_color = Color(1.0, 0.7, 0.2)
+11 -40
View File
@@ -21,45 +21,16 @@ func _init() -> void:
penetration_damage_penalty = 0.5
func _build_model() -> void:
position = Vector3(0.3, -0.3, -0.8)
var mat_green = StandardMaterial3D.new()
mat_green.albedo_color = Color(0.1, 0.4, 0.1)
var mat_black = StandardMaterial3D.new()
mat_black.albedo_color = Color(0.1, 0.1, 0.1)
# Body
var body = MeshInstance3D.new()
var b_mesh = BoxMesh.new()
b_mesh.size = Vector3(0.06, 0.12, 0.6)
b_mesh.material = mat_green
body.mesh = b_mesh
model_root.add_child(body)
# Scope
var scope = MeshInstance3D.new()
var s_mesh = CylinderMesh.new()
s_mesh.top_radius = 0.03
s_mesh.bottom_radius = 0.03
s_mesh.height = 0.25
s_mesh.material = mat_black
scope.mesh = s_mesh
scope.rotation.x = deg_to_rad(90)
scope.position = Vector3(0, 0.09, -0.1)
model_root.add_child(scope)
# Long Barrel
var barrel = MeshInstance3D.new()
var bar_mesh = CylinderMesh.new()
bar_mesh.top_radius = 0.015
bar_mesh.bottom_radius = 0.02
bar_mesh.height = 0.6
bar_mesh.material = mat_black
barrel.mesh = bar_mesh
barrel.rotation.x = deg_to_rad(90)
barrel.position = Vector3(0, 0.04, -0.6)
model_root.add_child(barrel)
position = Vector3(0.3, -0.3, -0.729)
var model_scene = load("res://assets/weapons/awp/source/L118a1.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.275, 0.275, 0.275)
model.rotation_degrees.y = 90
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Audio
fire_sound = AudioStreamPlayer3D.new()
fire_sound.bus = "SFX"
@@ -85,7 +56,7 @@ func _process(delta: float) -> void:
rig.set_weapon_fov(ads_fov if is_ads else -1.0)
# Move model slightly based on ADS
var target_pos = Vector3(0.0, -0.15, -0.6) if is_ads else Vector3(0.3, -0.3, -0.8)
var target_pos = Vector3(0.0, -0.15, -0.608) if is_ads else Vector3(0.3, -0.3, -0.729)
position = position.lerp(target_pos, 15.0 * delta)
func _input(event: InputEvent) -> void:
@@ -110,4 +81,4 @@ func unequip() -> void:
if rig and rig.has_method("set_weapon_fov"):
rig.set_weapon_fov(-1.0)
target_fov = default_fov
position = Vector3(0.3, -0.3, -0.8)
position = Vector3(0.3, -0.3, -0.729)
+10 -40
View File
@@ -23,46 +23,16 @@ func _ready() -> void:
default_fov = camera.fov
func _build_model() -> void:
position = Vector3(0.3, -0.3, -0.7)
var body_mat = StandardMaterial3D.new()
body_mat.albedo_color = Color(0.15, 0.25, 0.15) # Olive drab
var black_mat = StandardMaterial3D.new()
black_mat.albedo_color = Color(0.05, 0.05, 0.05)
# Receiver
var receiver = MeshInstance3D.new()
var rec_mesh = BoxMesh.new()
rec_mesh.size = Vector3(0.05, 0.12, 0.4)
rec_mesh.material = body_mat
receiver.mesh = rec_mesh
receiver.position = Vector3(0, 0, 0.1)
model_root.add_child(receiver)
# Barrel
var barrel = MeshInstance3D.new()
var b_mesh = CylinderMesh.new()
b_mesh.top_radius = 0.01
b_mesh.bottom_radius = 0.01
b_mesh.height = 0.6
b_mesh.material = black_mat
barrel.mesh = b_mesh
barrel.rotation.x = deg_to_rad(90)
barrel.position = Vector3(0, 0.03, -0.3)
model_root.add_child(barrel)
# Scope
var scope = MeshInstance3D.new()
var scope_mesh = CylinderMesh.new()
scope_mesh.top_radius = 0.02
scope_mesh.bottom_radius = 0.02
scope_mesh.height = 0.2
scope_mesh.material = black_mat
scope.mesh = scope_mesh
scope.rotation.x = deg_to_rad(90)
scope.position = Vector3(0, 0.08, 0.05)
model_root.add_child(scope)
position = Vector3(0.3, -0.3, -0.49)
var model_scene = load("res://assets/weapons/dmr/source/M14 .blend")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.1875, 0.1875, 0.1875)
model.rotation_degrees.y = 90
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Muzzle Flash
muzzle_flash = OmniLight3D.new()
muzzle_flash.light_color = Color(1.0, 0.8, 0.4)
@@ -125,4 +95,4 @@ func unequip() -> void:
var rig = camera.get_parent()
if rig and rig.has_method("set_weapon_fov"):
rig.set_weapon_fov(-1.0)
position = Vector3(0.3, -0.3, -0.7)
position = Vector3(0.3, -0.3, -0.49)
+13 -40
View File
@@ -20,46 +20,19 @@ func _ready() -> void:
_build_model()
func _build_model() -> void:
# Simple FPS weapon placement (bottom right)
position = Vector3(0.3, -0.3, -0.6)
model_root = Node3D.new()
add_child(model_root)
# Stock
var stock = MeshInstance3D.new()
var stock_mesh = BoxMesh.new()
stock_mesh.size = Vector3(0.1, 0.1, 0.4)
var stock_mat = StandardMaterial3D.new()
stock_mat.albedo_color = Color(0.4, 0.2, 0.1) # Brown wood
stock_mesh.material = stock_mat
stock.mesh = stock_mesh
stock.position = Vector3(0, 0, 0.2)
model_root.add_child(stock)
# Left Barrel
var barrel_l = MeshInstance3D.new()
var barrel_mesh = CylinderMesh.new()
barrel_mesh.top_radius = 0.03
barrel_mesh.bottom_radius = 0.03
barrel_mesh.height = 0.6
var barrel_mat = StandardMaterial3D.new()
barrel_mat.albedo_color = Color(0.2, 0.2, 0.2) # Dark grey metal
barrel_mat.metallic = 0.8
barrel_mat.roughness = 0.4
barrel_mesh.material = barrel_mat
barrel_l.mesh = barrel_mesh
barrel_l.rotation.x = deg_to_rad(90)
barrel_l.position = Vector3(-0.04, 0.03, -0.1)
model_root.add_child(barrel_l)
# Right Barrel
var barrel_r = MeshInstance3D.new()
barrel_r.mesh = barrel_mesh
barrel_r.rotation.x = deg_to_rad(90)
barrel_r.position = Vector3(0.04, 0.03, -0.1)
model_root.add_child(barrel_r)
if not model_root:
model_root = Node3D.new()
add_child(model_root)
position = Vector3(0.3, -0.3, -0.690)
var model_scene = load("res://assets/weapons/double barrel/source/Double Deuce.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.275, 0.275, 0.275)
model.rotation_degrees.y = 90
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Muzzle Flash Light
muzzle_flash = OmniLight3D.new()
muzzle_flash.light_color = Color(1.0, 0.8, 0.3)
@@ -222,7 +195,7 @@ func _shoot_hitscan() -> void:
t_mesh_inst.mesh = t_mesh
t_mesh_inst.rotation.x = deg_to_rad(90)
t_mesh_inst.position = Vector3(0, 0, -0.5)
t_mesh_inst.position = Vector3(0, 0, -0.719)
tracer.add_child(t_mesh_inst)
tracer.target_position = final_target
+15 -38
View File
@@ -13,7 +13,7 @@ var model_root: Node3D
var auto_switch_slot: int = -1
var is_swinging: bool = false
var default_pos: Vector3 = Vector3(0.3, -0.3, -0.6)
var default_pos: Vector3 = Vector3(0.3, -0.3, -0.945)
var fire_sound: AudioStreamPlayer3D
@@ -26,43 +26,20 @@ func _ready() -> void:
position = default_pos
func _build_model() -> void:
var mat_handle = StandardMaterial3D.new()
mat_handle.albedo_color = Color(0.1, 0.1, 0.1)
var model_scene = load("res://assets/weapons/knife/source/Knife.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.125, 0.125, 0.125)
model.rotation_degrees.y = 270
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
var mat_blade = StandardMaterial3D.new()
mat_blade.albedo_color = Color(0.8, 0.8, 0.8)
mat_blade.metallic = 0.8
mat_blade.roughness = 0.2
var mat = StandardMaterial3D.new()
mat.albedo_texture = load("res://assets/weapons/knife/textures/Color Palette.png")
# Handle
var handle = MeshInstance3D.new()
var h_mesh = CylinderMesh.new()
h_mesh.top_radius = 0.02
h_mesh.bottom_radius = 0.02
h_mesh.height = 0.15
h_mesh.material = mat_handle
handle.mesh = h_mesh
handle.rotation.x = deg_to_rad(90)
handle.position = Vector3(0, 0, 0)
model_root.add_child(handle)
# Blade
var blade = MeshInstance3D.new()
var b_mesh = BoxMesh.new()
b_mesh.size = Vector3(0.01, 0.04, 0.25)
b_mesh.material = mat_blade
blade.mesh = b_mesh
blade.position = Vector3(0, 0, -0.2)
model_root.add_child(blade)
# Guard
var guard = MeshInstance3D.new()
var g_mesh = BoxMesh.new()
g_mesh.size = Vector3(0.06, 0.05, 0.01)
g_mesh.material = mat_handle
guard.mesh = g_mesh
guard.position = Vector3(0, 0, -0.075)
model_root.add_child(guard)
for child in model.find_children("*", "MeshInstance3D", true, false):
child.set_surface_override_material(0, mat)
# Audio (reusing shotgun reload or something generic for now, ideally a swish)
fire_sound = AudioStreamPlayer3D.new()
@@ -106,11 +83,11 @@ func _swing() -> void:
var tween = create_tween()
# Windup: Move to the right, point the blade left and tilt it
tween.tween_property(self, "position", Vector3(0.6, -0.2, -0.4), 0.1)
tween.tween_property(self, "position", Vector3(0.6, -0.2, -0.630), 0.1)
tween.parallel().tween_property(self, "rotation", Vector3(deg_to_rad(10), deg_to_rad(80), deg_to_rad(-40)), 0.1)
# Swipe: Move across the screen to the left quickly
tween.tween_property(self, "position", Vector3(-0.6, -0.3, -0.5), 0.1)
tween.tween_property(self, "position", Vector3(-0.6, -0.3, -0.788), 0.1)
tween.parallel().tween_property(self, "rotation", Vector3(deg_to_rad(10), deg_to_rad(110), deg_to_rad(-60)), 0.1)
# Return to idle
+9 -36
View File
@@ -14,43 +14,16 @@ func _init() -> void:
spread_angle = 0.01
func _build_model() -> void:
position = Vector3(0.3, -0.3, -0.6)
var grey_mat = StandardMaterial3D.new()
grey_mat.albedo_color = Color(0.25, 0.25, 0.25)
var dark_mat = StandardMaterial3D.new()
dark_mat.albedo_color = Color(0.1, 0.1, 0.1)
# Receiver
var receiver = MeshInstance3D.new()
var rec_mesh = BoxMesh.new()
rec_mesh.size = Vector3(0.05, 0.14, 0.25)
rec_mesh.material = grey_mat
receiver.mesh = rec_mesh
receiver.position = Vector3(0, 0, 0.1)
model_root.add_child(receiver)
# Barrel
var barrel = MeshInstance3D.new()
var b_mesh = CylinderMesh.new()
b_mesh.top_radius = 0.012
b_mesh.bottom_radius = 0.012
b_mesh.height = 0.4
b_mesh.material = dark_mat
barrel.mesh = b_mesh
barrel.rotation.x = deg_to_rad(90)
barrel.position = Vector3(0, 0.04, -0.2)
model_root.add_child(barrel)
# Handguard
var guard = MeshInstance3D.new()
var g_mesh = BoxMesh.new()
g_mesh.size = Vector3(0.06, 0.07, 0.22)
g_mesh.material = dark_mat
guard.mesh = g_mesh
guard.position = Vector3(0, 0.04, -0.1)
model_root.add_child(guard)
position = Vector3(0.3, -0.3, -0.656)
var model_scene = load("res://assets/weapons/m4/source/M4a4.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.275, 0.275, 0.275)
model.rotation_degrees.y = 90
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Muzzle Flash
muzzle_flash = OmniLight3D.new()
muzzle_flash.light_color = Color(1.0, 0.8, 0.4)
+13 -13
View File
@@ -17,21 +17,21 @@ func _init() -> void:
projectile_gravity = 15.0 # Heavy drop
func _build_model() -> void:
position = Vector3(0.3, -0.4, -0.5)
position = Vector3(0.5, -0.4, -0.8)
var mat_olive = StandardMaterial3D.new()
mat_olive.albedo_color = Color(0.2, 0.3, 0.1)
var model_scene = load("res://assets/weapons/mortar/source/scene.gltf")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.0025, 0.0025, 0.0025)
model.rotation_degrees.y = -90
model.position.y -= 0.2
# Thick short tube
var body = MeshInstance3D.new()
var b_mesh = CylinderMesh.new()
b_mesh.top_radius = 0.12
b_mesh.bottom_radius = 0.12
b_mesh.height = 0.5
b_mesh.material = mat_olive
body.mesh = b_mesh
body.rotation.x = deg_to_rad(90)
model_root.add_child(body)
# Aggressively strip any rogue nodes from the GLTF that could ruin the scene
for node in model.find_children("*", "", true, false):
if node is Light3D or node is Camera3D or node is WorldEnvironment or node is ReflectionProbe:
if node is Node3D: node.visible = false
node.queue_free()
# Audio
fire_sound = AudioStreamPlayer3D.new()
+9 -45
View File
@@ -14,52 +14,16 @@ func _init() -> void:
spread_angle = 0.025
func _build_model() -> void:
position = Vector3(0.25, -0.25, -0.5)
var black_mat = StandardMaterial3D.new()
black_mat.albedo_color = Color(0.05, 0.05, 0.05)
# Receiver
var receiver = MeshInstance3D.new()
var rec_mesh = BoxMesh.new()
rec_mesh.size = Vector3(0.04, 0.1, 0.2)
rec_mesh.material = black_mat
receiver.mesh = rec_mesh
receiver.position = Vector3(0, 0, 0)
model_root.add_child(receiver)
# Grip
var grip = MeshInstance3D.new()
var g_mesh = BoxMesh.new()
g_mesh.size = Vector3(0.03, 0.12, 0.04)
g_mesh.material = black_mat
grip.mesh = g_mesh
grip.position = Vector3(0, -0.06, 0.08)
grip.rotation.x = deg_to_rad(10)
model_root.add_child(grip)
# Foregrip
var fgrip = MeshInstance3D.new()
var fg_mesh = BoxMesh.new()
fg_mesh.size = Vector3(0.03, 0.08, 0.03)
fg_mesh.material = black_mat
fgrip.mesh = fg_mesh
fgrip.position = Vector3(0, -0.05, -0.06)
fgrip.rotation.x = deg_to_rad(-10)
model_root.add_child(fgrip)
# Barrel
var barrel = MeshInstance3D.new()
var b_mesh = CylinderMesh.new()
b_mesh.top_radius = 0.008
b_mesh.bottom_radius = 0.008
b_mesh.height = 0.1
b_mesh.material = black_mat
barrel.mesh = b_mesh
barrel.rotation.x = deg_to_rad(90)
barrel.position = Vector3(0, 0.02, -0.15)
model_root.add_child(barrel)
position = Vector3(0.25, -0.25, -0.35)
var model_scene = load("res://assets/weapons/mp7/source/HK MP7.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.08, 0.08, 0.08)
model.rotation_degrees.y = 90
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Muzzle Flash
muzzle_flash = OmniLight3D.new()
muzzle_flash.light_color = Color(1.0, 0.9, 0.5)
+20 -45
View File
@@ -16,54 +16,29 @@ func _init() -> void:
projectile_gravity = 5.0 # Slight drop
func _build_model() -> void:
position = Vector3(0.3, -0.3, -0.6)
position = Vector3(0.3, -0.3, -0.604)
var yellow_mat = StandardMaterial3D.new()
yellow_mat.albedo_color = Color(0.8, 0.7, 0.1) # Construction yellow
var dark_mat = StandardMaterial3D.new()
dark_mat.albedo_color = Color(0.2, 0.2, 0.2)
var model_scene = load("res://assets/weapons/nailgun/source/Sopra.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.06, 0.06, 0.06)
model.rotation_degrees.y = 180
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Body
var body = MeshInstance3D.new()
var b_mesh = BoxMesh.new()
b_mesh.size = Vector3(0.06, 0.16, 0.25)
b_mesh.material = yellow_mat
body.mesh = b_mesh
body.position = Vector3(0, 0.02, 0)
model_root.add_child(body)
var mat = StandardMaterial3D.new()
mat.albedo_texture = load("res://assets/weapons/nailgun/textures/Base_texture.png")
mat.normal_enabled = true
mat.normal_texture = load("res://assets/weapons/nailgun/textures/Normal_texture.png")
mat.metallic = 1.0
mat.metallic_texture = load("res://assets/weapons/nailgun/textures/Metallic_texture.png")
mat.roughness_texture = load("res://assets/weapons/nailgun/textures/Roughness_texture.png")
mat.ao_enabled = true
mat.ao_texture = load("res://assets/weapons/nailgun/textures/internal_ground_ao_texture.jpeg")
# Handle
var handle = MeshInstance3D.new()
var h_mesh = BoxMesh.new()
h_mesh.size = Vector3(0.04, 0.1, 0.04)
h_mesh.material = dark_mat
handle.mesh = h_mesh
handle.position = Vector3(0, -0.1, 0.05)
model_root.add_child(handle)
# Mag (Nail Drum)
var drum = MeshInstance3D.new()
var d_mesh = CylinderMesh.new()
d_mesh.top_radius = 0.05
d_mesh.bottom_radius = 0.05
d_mesh.height = 0.1
d_mesh.material = dark_mat
drum.mesh = d_mesh
drum.rotation.z = deg_to_rad(90)
drum.position = Vector3(0, -0.06, -0.05)
model_root.add_child(drum)
# Barrel
var barrel = MeshInstance3D.new()
var bar_mesh = CylinderMesh.new()
bar_mesh.top_radius = 0.01
bar_mesh.bottom_radius = 0.01
bar_mesh.height = 0.15
bar_mesh.material = dark_mat
barrel.mesh = bar_mesh
barrel.rotation.x = deg_to_rad(90)
barrel.position = Vector3(0, 0.05, -0.2)
model_root.add_child(barrel)
for child in model.find_children("*", "MeshInstance3D", true, false):
child.set_surface_override_material(0, mat)
# Muzzle Flash (Sparks)
muzzle_flash = OmniLight3D.new()
+13 -27
View File
@@ -17,36 +17,22 @@ func _init() -> void:
projectile_gravity = 0.0
func _build_model() -> void:
position = Vector3(0.3, -0.3, -0.6)
position = Vector3(0.3, -0.3, -0.656)
var white_mat = StandardMaterial3D.new()
white_mat.albedo_color = Color(0.8, 0.8, 0.9)
var glow_mat = StandardMaterial3D.new()
glow_mat.albedo_color = Color(0.2, 0.5, 1.0)
glow_mat.emission_enabled = true
glow_mat.emission = Color(0.2, 0.5, 1.0)
glow_mat.emission_energy_multiplier = 4.0
var model_scene = load("res://assets/weapons/plasmagun/source/Plasma rifle.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(1.0, 1.0, 1.0)
model.rotation_degrees.y = 180
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Body
var body = MeshInstance3D.new()
var b_mesh = BoxMesh.new()
b_mesh.size = Vector3(0.08, 0.14, 0.35)
b_mesh.material = white_mat
body.mesh = b_mesh
body.position = Vector3(0, 0, 0.05)
model_root.add_child(body)
var mat = StandardMaterial3D.new()
mat.albedo_texture = load("res://assets/weapons/plasmagun/textures/Plasma_rifle.png")
# Glow Core
var core = MeshInstance3D.new()
var c_mesh = CylinderMesh.new()
c_mesh.top_radius = 0.02
c_mesh.bottom_radius = 0.02
c_mesh.height = 0.4
c_mesh.material = glow_mat
core.mesh = c_mesh
core.rotation.x = deg_to_rad(90)
core.position = Vector3(0, 0.02, -0.15)
model_root.add_child(core)
for child in model.find_children("*", "MeshInstance3D", true, false):
child.set_surface_override_material(0, mat)
# Muzzle Flash
muzzle_flash = OmniLight3D.new()
+9 -27
View File
@@ -17,34 +17,16 @@ func _init() -> void:
projectile_gravity = 0.0 # Flies straight
func _build_model() -> void:
position = Vector3(0.4, -0.4, -0.6)
var mat_grey = StandardMaterial3D.new()
mat_grey.albedo_color = Color(0.3, 0.3, 0.3)
# Main Tube
var body = MeshInstance3D.new()
var b_mesh = CylinderMesh.new()
b_mesh.top_radius = 0.08
b_mesh.bottom_radius = 0.08
b_mesh.height = 0.8
b_mesh.material = mat_grey
body.mesh = b_mesh
body.rotation.x = deg_to_rad(90)
model_root.add_child(body)
# Back cone
var cone = MeshInstance3D.new()
var c_mesh = CylinderMesh.new()
c_mesh.top_radius = 0.08
c_mesh.bottom_radius = 0.04
c_mesh.height = 0.2
c_mesh.material = mat_grey
cone.mesh = c_mesh
cone.rotation.x = deg_to_rad(90)
cone.position = Vector3(0, 0, 0.5)
model_root.add_child(cone)
position = Vector3(0.4, -0.4, -0.945)
var model_scene = load("res://assets/weapons/rocket launcher/source/Law72.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(0.25, 0.25, 0.25)
model.rotation_degrees.y = 90
for light in model.find_children("*", "Light3D", true, false):
light.queue_free()
# Muzzle Flash
muzzle_flash = OmniLight3D.new()
muzzle_flash.light_color = Color(1.0, 0.6, 0.2)
+13 -12
View File
@@ -20,18 +20,19 @@ func _init() -> void:
projectile_speed = 25.0 # Slower so they have time to home
func _build_model() -> void:
position = Vector3(0.4, -0.4, -0.6)
position = Vector3(0.4, -0.4, -0.656)
var mat_box = StandardMaterial3D.new()
mat_box.albedo_color = Color(0.2, 0.4, 0.5)
var model_scene = load("res://assets/weapons/swarm launcher/source/LOD_0.fbx")
if model_scene:
var model = model_scene.instantiate()
model_root.add_child(model)
model.scale = Vector3(1.0, 1.0, 1.0)
model.rotation_degrees.y = -90
model.position.y -= 0.6 # Move model down without moving arms
# Box launcher
var body = MeshInstance3D.new()
var b_mesh = BoxMesh.new()
b_mesh.size = Vector3(0.2, 0.2, 0.4)
b_mesh.material = mat_box
body.mesh = b_mesh
model_root.add_child(body)
# Strip lights
for child in model.find_children("*", "Light3D", true, false):
child.queue_free()
# Laser
targeting_laser = MeshInstance3D.new()
@@ -46,7 +47,7 @@ func _build_model() -> void:
l_mesh.material = l_mat
targeting_laser.mesh = l_mesh
targeting_laser.rotation.x = deg_to_rad(90)
targeting_laser.position = Vector3(0, 0, -150.0)
targeting_laser.position = Vector3(0, 0, -234.375)
targeting_laser.visible = false
model_root.add_child(targeting_laser)
@@ -123,7 +124,7 @@ func _fire() -> void:
var tween = create_tween()
tween.tween_property(muzzle_flash, "light_energy", 0.0, 0.1)
var _origin = camera.global_position + camera.global_transform.basis * Vector3(0.4, -0.2, -0.6)
var _origin = camera.global_position + camera.global_transform.basis * Vector3(0.4, -0.2, -0.938)
var base_dir = -camera.global_transform.basis.z
_current_aim_dir = base_dir
+12 -1
View File
@@ -58,7 +58,18 @@ func _setup_viewmodel_viewport() -> void:
func _process(_delta: float) -> void:
if is_instance_valid(vm_camera) and is_instance_valid(camera):
vm_camera.global_transform = camera.global_transform
vm_camera.fov = camera.fov
var target_fov = 70.0
if is_instance_valid(player) and "velocity" in player:
var hspeed = Vector2(player.velocity.x, player.velocity.z).length()
if hspeed <= 11.0:
var speed_factor = hspeed / 11.0
target_fov = lerpf(70.0, 72.0, speed_factor)
else:
var over_speed_factor = clampf((hspeed - 11.0) / 19.0, 0.0, 1.0) # maxes out at 30 m/s
target_fov = lerpf(72.0, 80.0, over_speed_factor)
vm_camera.fov = lerpf(vm_camera.fov, target_fov, 10.0 * _delta)
func _build_loadout() -> void:
var is_auth = false