Feat/11 adding better fundamental weapon models and some textures #16
@@ -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/)
|
||||||
|
After Width: | Height: | Size: 802 B |
|
After Width: | Height: | Size: 556 B |
@@ -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/)
|
||||||
|
After Width: | Height: | Size: 4.8 MiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 5.3 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 544 KiB |
|
After Width: | Height: | Size: 268 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 591 KiB |
|
After Width: | Height: | Size: 3.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
@@ -20,8 +20,8 @@ func _ready() -> void:
|
|||||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||||
camera = get_node_or_null("Camera3D")
|
camera = get_node_or_null("Camera3D")
|
||||||
if camera and params:
|
if camera and params:
|
||||||
camera.fov = params.base_fov
|
camera.fov = SettingsManager.world_fov
|
||||||
_target_fov = params.base_fov
|
_target_fov = SettingsManager.world_fov
|
||||||
# Explicitly enable callbacks — set_script() at runtime doesn't auto-register them
|
# Explicitly enable callbacks — set_script() at runtime doesn't auto-register them
|
||||||
set_process_input(true)
|
set_process_input(true)
|
||||||
set_process(true)
|
set_process(true)
|
||||||
@@ -29,7 +29,7 @@ func _ready() -> void:
|
|||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
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
|
var sens = SettingsManager.ads_sensitivity if is_ads else SettingsManager.mouse_sensitivity
|
||||||
# Yaw: rotate the player (parent)
|
# Yaw: rotate the player (parent)
|
||||||
get_parent().rotate_y(-event.relative.x * sens)
|
get_parent().rotate_y(-event.relative.x * sens)
|
||||||
@@ -53,12 +53,12 @@ func _process(delta: float) -> void:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# ── FOV kick ──────────────────────────────────────────────────────────
|
# ── FOV kick ──────────────────────────────────────────────────────────
|
||||||
_target_fov = params.base_fov
|
_target_fov = SettingsManager.world_fov
|
||||||
var hspeed := Vector2(player.velocity.x, player.velocity.z).length()
|
var hspeed := Vector2(player.velocity.x, player.velocity.z).length()
|
||||||
|
|
||||||
# Smoothly scale FOV based on speed above walking speed
|
# 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)
|
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:
|
if _weapon_fov_override > 0.0:
|
||||||
_target_fov = _weapon_fov_override
|
_target_fov = _weapon_fov_override
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ var default_bindings = {
|
|||||||
"weapon_2": {"type": "key", "code": KEY_2},
|
"weapon_2": {"type": "key", "code": KEY_2},
|
||||||
"weapon_3": {"type": "key", "code": KEY_3},
|
"weapon_3": {"type": "key", "code": KEY_3},
|
||||||
"weapon_4": {"type": "key", "code": KEY_4},
|
"weapon_4": {"type": "key", "code": KEY_4},
|
||||||
|
"toggle_flashlight": {"type": "key", "code": KEY_T},
|
||||||
}
|
}
|
||||||
|
|
||||||
var current_bindings = {}
|
var current_bindings = {}
|
||||||
@@ -29,6 +30,7 @@ var show_debug_ui: bool = false
|
|||||||
var username: String = "Player"
|
var username: String = "Player"
|
||||||
var mouse_sensitivity: float = 0.002
|
var mouse_sensitivity: float = 0.002
|
||||||
var ads_sensitivity: float = 0.001
|
var ads_sensitivity: float = 0.001
|
||||||
|
var world_fov: float = 90.0
|
||||||
|
|
||||||
# Video Settings
|
# Video Settings
|
||||||
var display_mode: int = DisplayServer.WINDOW_MODE_WINDOWED
|
var display_mode: int = DisplayServer.WINDOW_MODE_WINDOWED
|
||||||
@@ -135,6 +137,7 @@ func _save_settings() -> void:
|
|||||||
"username": username,
|
"username": username,
|
||||||
"mouse_sensitivity": mouse_sensitivity,
|
"mouse_sensitivity": mouse_sensitivity,
|
||||||
"ads_sensitivity": ads_sensitivity,
|
"ads_sensitivity": ads_sensitivity,
|
||||||
|
"world_fov": world_fov,
|
||||||
"display_mode": display_mode,
|
"display_mode": display_mode,
|
||||||
"resolution_index": resolution_index,
|
"resolution_index": resolution_index,
|
||||||
"fps_cap": fps_cap,
|
"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("show_debug_ui"): show_debug_ui = data["show_debug_ui"]
|
||||||
if data.has("mouse_sensitivity"): mouse_sensitivity = data["mouse_sensitivity"]
|
if data.has("mouse_sensitivity"): mouse_sensitivity = data["mouse_sensitivity"]
|
||||||
if data.has("ads_sensitivity"): ads_sensitivity = data["ads_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("display_mode"): display_mode = data["display_mode"]
|
||||||
if data.has("resolution_index"): resolution_index = data["resolution_index"]
|
if data.has("resolution_index"): resolution_index = data["resolution_index"]
|
||||||
if data.has("fps_cap"): fps_cap = data["fps_cap"]
|
if data.has("fps_cap"): fps_cap = data["fps_cap"]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ signal chain_updated(count: int, bonus: float)
|
|||||||
var _machine: MovementStateMachine = null
|
var _machine: MovementStateMachine = null
|
||||||
var head_pivot: Node3D = null # FPSCameraRig node
|
var head_pivot: Node3D = null # FPSCameraRig node
|
||||||
var camera: Camera3D = null
|
var camera: Camera3D = null
|
||||||
|
var flashlight: SpotLight3D = null
|
||||||
var _damage_layer: CanvasLayer = null
|
var _damage_layer: CanvasLayer = null
|
||||||
|
|
||||||
# Health and Shield
|
# Health and Shield
|
||||||
@@ -82,6 +83,17 @@ func _ready() -> void:
|
|||||||
if head_pivot:
|
if head_pivot:
|
||||||
camera = head_pivot.get_node_or_null("Camera3D")
|
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_audio()
|
||||||
_setup_grapple()
|
_setup_grapple()
|
||||||
|
|
||||||
@@ -652,6 +664,9 @@ func _physics_process(_delta: float) -> void:
|
|||||||
var input_grapple = Input.is_action_pressed("grapple")
|
var input_grapple = Input.is_action_pressed("grapple")
|
||||||
var input_grapple_just = Input.is_action_just_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)
|
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()
|
var speed = velocity.length()
|
||||||
|
|||||||
@@ -102,3 +102,10 @@ scoreboard={
|
|||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
rendering_device/driver.windows="d3d12"
|
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
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://rwsrtgh08bhb
|
||||||
@@ -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"
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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")
|
||||||
@@ -87,17 +87,7 @@ func _ready() -> void:
|
|||||||
cards_hbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
cards_hbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
_level_selector_panel.add_child(cards_hbox)
|
_level_selector_panel.add_child(cards_hbox)
|
||||||
|
|
||||||
# Level 1: Dust 2
|
_load_maps_modularly(cards_hbox, false, Vector2(300, 400))
|
||||||
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)
|
|
||||||
|
|
||||||
var ls_spacer = Control.new()
|
var ls_spacer = Control.new()
|
||||||
ls_spacer.custom_minimum_size = Vector2(0, 20)
|
ls_spacer.custom_minimum_size = Vector2(0, 20)
|
||||||
@@ -224,8 +214,7 @@ func _ready() -> void:
|
|||||||
hl_cards.add_theme_constant_override("separation", 20)
|
hl_cards.add_theme_constant_override("separation", 20)
|
||||||
hl_left.add_child(hl_cards)
|
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))
|
_load_maps_modularly(hl_cards, true, Vector2(200, 250))
|
||||||
_build_level_card(hl_cards, "Test Level", "res://scenes/test_level/test_level.tscn", tl_gradient, true, Vector2(200, 250))
|
|
||||||
|
|
||||||
var mode_label = Label.new()
|
var mode_label = Label.new()
|
||||||
mode_label.text = "Gamemode"
|
mode_label.text = "Gamemode"
|
||||||
@@ -277,6 +266,33 @@ func _ready() -> void:
|
|||||||
_host_lobby_players_list.add_theme_font_size_override("font_size", 24)
|
_host_lobby_players_list.add_theme_font_size_override("font_size", 24)
|
||||||
hl_right.add_child(_host_lobby_players_list)
|
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:
|
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()
|
var btn = TextureButton.new()
|
||||||
btn.custom_minimum_size = card_size
|
btn.custom_minimum_size = card_size
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ var editing_index: int = -1
|
|||||||
# Settings UI
|
# Settings UI
|
||||||
var settings_editor: Control
|
var settings_editor: Control
|
||||||
var settings_back_btn: Button
|
var settings_back_btn: Button
|
||||||
|
var _world_fov_slider: HSlider
|
||||||
|
var _world_fov_input: LineEdit
|
||||||
var _debug_ui_checkbox: CheckButton
|
var _debug_ui_checkbox: CheckButton
|
||||||
var _mouse_sens_slider: HSlider
|
var _mouse_sens_slider: HSlider
|
||||||
var _mouse_sens_input: LineEdit
|
var _mouse_sens_input: LineEdit
|
||||||
@@ -279,6 +281,43 @@ func _build_ui() -> void:
|
|||||||
)
|
)
|
||||||
user_hbox.add_child(user_save)
|
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()
|
var spacer_gen = Control.new()
|
||||||
spacer_gen.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
spacer_gen.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
general_tab.add_child(spacer_gen)
|
general_tab.add_child(spacer_gen)
|
||||||
@@ -286,7 +325,9 @@ func _build_ui() -> void:
|
|||||||
var reset_gen_btn = Button.new()
|
var reset_gen_btn = Button.new()
|
||||||
reset_gen_btn.text = "Reset to Defaults"
|
reset_gen_btn.text = "Reset to Defaults"
|
||||||
reset_gen_btn.pressed.connect(func():
|
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)
|
general_tab.add_child(reset_gen_btn)
|
||||||
|
|
||||||
@@ -743,6 +784,9 @@ func _show_settings() -> void:
|
|||||||
_debug_ui_checkbox.button_pressed = SettingsManager.show_debug_ui
|
_debug_ui_checkbox.button_pressed = SettingsManager.show_debug_ui
|
||||||
if _show_fps_checkbox:
|
if _show_fps_checkbox:
|
||||||
_show_fps_checkbox.button_pressed = SettingsManager.show_fps
|
_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:
|
if _mouse_sens_slider:
|
||||||
var display_val = round(SettingsManager.mouse_sensitivity * 10000.0)
|
var display_val = round(SettingsManager.mouse_sensitivity * 10000.0)
|
||||||
_mouse_sens_slider.value = display_val
|
_mouse_sens_slider.value = display_val
|
||||||
|
|||||||
@@ -14,52 +14,16 @@ func _init() -> void:
|
|||||||
spread_angle = 0.015
|
spread_angle = 0.015
|
||||||
|
|
||||||
func _build_model() -> void:
|
func _build_model() -> void:
|
||||||
# Position
|
position = Vector3(0.3, -0.3, -0.590)
|
||||||
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)
|
|
||||||
|
|
||||||
|
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
|
||||||
muzzle_flash = OmniLight3D.new()
|
muzzle_flash = OmniLight3D.new()
|
||||||
muzzle_flash.light_color = Color(1.0, 0.7, 0.2)
|
muzzle_flash.light_color = Color(1.0, 0.7, 0.2)
|
||||||
|
|||||||
@@ -21,45 +21,16 @@ func _init() -> void:
|
|||||||
penetration_damage_penalty = 0.5
|
penetration_damage_penalty = 0.5
|
||||||
|
|
||||||
func _build_model() -> void:
|
func _build_model() -> void:
|
||||||
position = Vector3(0.3, -0.3, -0.8)
|
position = Vector3(0.3, -0.3, -0.729)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
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
|
# Audio
|
||||||
fire_sound = AudioStreamPlayer3D.new()
|
fire_sound = AudioStreamPlayer3D.new()
|
||||||
fire_sound.bus = "SFX"
|
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)
|
rig.set_weapon_fov(ads_fov if is_ads else -1.0)
|
||||||
|
|
||||||
# Move model slightly based on ADS
|
# 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)
|
position = position.lerp(target_pos, 15.0 * delta)
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
@@ -110,4 +81,4 @@ func unequip() -> void:
|
|||||||
if rig and rig.has_method("set_weapon_fov"):
|
if rig and rig.has_method("set_weapon_fov"):
|
||||||
rig.set_weapon_fov(-1.0)
|
rig.set_weapon_fov(-1.0)
|
||||||
target_fov = default_fov
|
target_fov = default_fov
|
||||||
position = Vector3(0.3, -0.3, -0.8)
|
position = Vector3(0.3, -0.3, -0.729)
|
||||||
|
|||||||
@@ -23,46 +23,16 @@ func _ready() -> void:
|
|||||||
default_fov = camera.fov
|
default_fov = camera.fov
|
||||||
|
|
||||||
func _build_model() -> void:
|
func _build_model() -> void:
|
||||||
position = Vector3(0.3, -0.3, -0.7)
|
position = Vector3(0.3, -0.3, -0.49)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
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
|
||||||
muzzle_flash = OmniLight3D.new()
|
muzzle_flash = OmniLight3D.new()
|
||||||
muzzle_flash.light_color = Color(1.0, 0.8, 0.4)
|
muzzle_flash.light_color = Color(1.0, 0.8, 0.4)
|
||||||
@@ -125,4 +95,4 @@ func unequip() -> void:
|
|||||||
var rig = camera.get_parent()
|
var rig = camera.get_parent()
|
||||||
if rig and rig.has_method("set_weapon_fov"):
|
if rig and rig.has_method("set_weapon_fov"):
|
||||||
rig.set_weapon_fov(-1.0)
|
rig.set_weapon_fov(-1.0)
|
||||||
position = Vector3(0.3, -0.3, -0.7)
|
position = Vector3(0.3, -0.3, -0.49)
|
||||||
|
|||||||
@@ -20,46 +20,19 @@ func _ready() -> void:
|
|||||||
_build_model()
|
_build_model()
|
||||||
|
|
||||||
func _build_model() -> void:
|
func _build_model() -> void:
|
||||||
# Simple FPS weapon placement (bottom right)
|
if not model_root:
|
||||||
position = Vector3(0.3, -0.3, -0.6)
|
|
||||||
|
|
||||||
model_root = Node3D.new()
|
model_root = Node3D.new()
|
||||||
add_child(model_root)
|
add_child(model_root)
|
||||||
|
position = Vector3(0.3, -0.3, -0.690)
|
||||||
|
|
||||||
# Stock
|
var model_scene = load("res://assets/weapons/double barrel/source/Double Deuce.fbx")
|
||||||
var stock = MeshInstance3D.new()
|
if model_scene:
|
||||||
var stock_mesh = BoxMesh.new()
|
var model = model_scene.instantiate()
|
||||||
stock_mesh.size = Vector3(0.1, 0.1, 0.4)
|
model_root.add_child(model)
|
||||||
var stock_mat = StandardMaterial3D.new()
|
model.scale = Vector3(0.275, 0.275, 0.275)
|
||||||
stock_mat.albedo_color = Color(0.4, 0.2, 0.1) # Brown wood
|
model.rotation_degrees.y = 90
|
||||||
stock_mesh.material = stock_mat
|
for light in model.find_children("*", "Light3D", true, false):
|
||||||
stock.mesh = stock_mesh
|
light.queue_free()
|
||||||
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)
|
|
||||||
|
|
||||||
# Muzzle Flash Light
|
# Muzzle Flash Light
|
||||||
muzzle_flash = OmniLight3D.new()
|
muzzle_flash = OmniLight3D.new()
|
||||||
muzzle_flash.light_color = Color(1.0, 0.8, 0.3)
|
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.mesh = t_mesh
|
||||||
t_mesh_inst.rotation.x = deg_to_rad(90)
|
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.add_child(t_mesh_inst)
|
||||||
|
|
||||||
tracer.target_position = final_target
|
tracer.target_position = final_target
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ var model_root: Node3D
|
|||||||
|
|
||||||
var auto_switch_slot: int = -1
|
var auto_switch_slot: int = -1
|
||||||
var is_swinging: bool = false
|
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
|
var fire_sound: AudioStreamPlayer3D
|
||||||
|
|
||||||
@@ -26,43 +26,20 @@ func _ready() -> void:
|
|||||||
position = default_pos
|
position = default_pos
|
||||||
|
|
||||||
func _build_model() -> void:
|
func _build_model() -> void:
|
||||||
var mat_handle = StandardMaterial3D.new()
|
var model_scene = load("res://assets/weapons/knife/source/Knife.fbx")
|
||||||
mat_handle.albedo_color = Color(0.1, 0.1, 0.1)
|
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()
|
var mat = StandardMaterial3D.new()
|
||||||
mat_blade.albedo_color = Color(0.8, 0.8, 0.8)
|
mat.albedo_texture = load("res://assets/weapons/knife/textures/Color Palette.png")
|
||||||
mat_blade.metallic = 0.8
|
|
||||||
mat_blade.roughness = 0.2
|
|
||||||
|
|
||||||
# Handle
|
for child in model.find_children("*", "MeshInstance3D", true, false):
|
||||||
var handle = MeshInstance3D.new()
|
child.set_surface_override_material(0, mat)
|
||||||
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)
|
|
||||||
|
|
||||||
# Audio (reusing shotgun reload or something generic for now, ideally a swish)
|
# Audio (reusing shotgun reload or something generic for now, ideally a swish)
|
||||||
fire_sound = AudioStreamPlayer3D.new()
|
fire_sound = AudioStreamPlayer3D.new()
|
||||||
@@ -106,11 +83,11 @@ func _swing() -> void:
|
|||||||
var tween = create_tween()
|
var tween = create_tween()
|
||||||
|
|
||||||
# Windup: Move to the right, point the blade left and tilt it
|
# 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)
|
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
|
# 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)
|
tween.parallel().tween_property(self, "rotation", Vector3(deg_to_rad(10), deg_to_rad(110), deg_to_rad(-60)), 0.1)
|
||||||
|
|
||||||
# Return to idle
|
# Return to idle
|
||||||
|
|||||||
@@ -14,43 +14,16 @@ func _init() -> void:
|
|||||||
spread_angle = 0.01
|
spread_angle = 0.01
|
||||||
|
|
||||||
func _build_model() -> void:
|
func _build_model() -> void:
|
||||||
position = Vector3(0.3, -0.3, -0.6)
|
position = Vector3(0.3, -0.3, -0.656)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
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
|
||||||
muzzle_flash = OmniLight3D.new()
|
muzzle_flash = OmniLight3D.new()
|
||||||
muzzle_flash.light_color = Color(1.0, 0.8, 0.4)
|
muzzle_flash.light_color = Color(1.0, 0.8, 0.4)
|
||||||
|
|||||||
@@ -17,21 +17,21 @@ func _init() -> void:
|
|||||||
projectile_gravity = 15.0 # Heavy drop
|
projectile_gravity = 15.0 # Heavy drop
|
||||||
|
|
||||||
func _build_model() -> void:
|
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()
|
var model_scene = load("res://assets/weapons/mortar/source/scene.gltf")
|
||||||
mat_olive.albedo_color = Color(0.2, 0.3, 0.1)
|
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
|
# Aggressively strip any rogue nodes from the GLTF that could ruin the scene
|
||||||
var body = MeshInstance3D.new()
|
for node in model.find_children("*", "", true, false):
|
||||||
var b_mesh = CylinderMesh.new()
|
if node is Light3D or node is Camera3D or node is WorldEnvironment or node is ReflectionProbe:
|
||||||
b_mesh.top_radius = 0.12
|
if node is Node3D: node.visible = false
|
||||||
b_mesh.bottom_radius = 0.12
|
node.queue_free()
|
||||||
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)
|
|
||||||
|
|
||||||
# Audio
|
# Audio
|
||||||
fire_sound = AudioStreamPlayer3D.new()
|
fire_sound = AudioStreamPlayer3D.new()
|
||||||
|
|||||||
@@ -14,52 +14,16 @@ func _init() -> void:
|
|||||||
spread_angle = 0.025
|
spread_angle = 0.025
|
||||||
|
|
||||||
func _build_model() -> void:
|
func _build_model() -> void:
|
||||||
position = Vector3(0.25, -0.25, -0.5)
|
position = Vector3(0.25, -0.25, -0.35)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
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
|
||||||
muzzle_flash = OmniLight3D.new()
|
muzzle_flash = OmniLight3D.new()
|
||||||
muzzle_flash.light_color = Color(1.0, 0.9, 0.5)
|
muzzle_flash.light_color = Color(1.0, 0.9, 0.5)
|
||||||
|
|||||||
@@ -16,54 +16,29 @@ func _init() -> void:
|
|||||||
projectile_gravity = 5.0 # Slight drop
|
projectile_gravity = 5.0 # Slight drop
|
||||||
|
|
||||||
func _build_model() -> void:
|
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()
|
var model_scene = load("res://assets/weapons/nailgun/source/Sopra.fbx")
|
||||||
yellow_mat.albedo_color = Color(0.8, 0.7, 0.1) # Construction yellow
|
if model_scene:
|
||||||
var dark_mat = StandardMaterial3D.new()
|
var model = model_scene.instantiate()
|
||||||
dark_mat.albedo_color = Color(0.2, 0.2, 0.2)
|
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 mat = StandardMaterial3D.new()
|
||||||
var body = MeshInstance3D.new()
|
mat.albedo_texture = load("res://assets/weapons/nailgun/textures/Base_texture.png")
|
||||||
var b_mesh = BoxMesh.new()
|
mat.normal_enabled = true
|
||||||
b_mesh.size = Vector3(0.06, 0.16, 0.25)
|
mat.normal_texture = load("res://assets/weapons/nailgun/textures/Normal_texture.png")
|
||||||
b_mesh.material = yellow_mat
|
mat.metallic = 1.0
|
||||||
body.mesh = b_mesh
|
mat.metallic_texture = load("res://assets/weapons/nailgun/textures/Metallic_texture.png")
|
||||||
body.position = Vector3(0, 0.02, 0)
|
mat.roughness_texture = load("res://assets/weapons/nailgun/textures/Roughness_texture.png")
|
||||||
model_root.add_child(body)
|
mat.ao_enabled = true
|
||||||
|
mat.ao_texture = load("res://assets/weapons/nailgun/textures/internal_ground_ao_texture.jpeg")
|
||||||
|
|
||||||
# Handle
|
for child in model.find_children("*", "MeshInstance3D", true, false):
|
||||||
var handle = MeshInstance3D.new()
|
child.set_surface_override_material(0, mat)
|
||||||
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)
|
|
||||||
|
|
||||||
# Muzzle Flash (Sparks)
|
# Muzzle Flash (Sparks)
|
||||||
muzzle_flash = OmniLight3D.new()
|
muzzle_flash = OmniLight3D.new()
|
||||||
|
|||||||
@@ -17,36 +17,22 @@ func _init() -> void:
|
|||||||
projectile_gravity = 0.0
|
projectile_gravity = 0.0
|
||||||
|
|
||||||
func _build_model() -> void:
|
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()
|
var model_scene = load("res://assets/weapons/plasmagun/source/Plasma rifle.fbx")
|
||||||
white_mat.albedo_color = Color(0.8, 0.8, 0.9)
|
if model_scene:
|
||||||
var glow_mat = StandardMaterial3D.new()
|
var model = model_scene.instantiate()
|
||||||
glow_mat.albedo_color = Color(0.2, 0.5, 1.0)
|
model_root.add_child(model)
|
||||||
glow_mat.emission_enabled = true
|
model.scale = Vector3(1.0, 1.0, 1.0)
|
||||||
glow_mat.emission = Color(0.2, 0.5, 1.0)
|
model.rotation_degrees.y = 180
|
||||||
glow_mat.emission_energy_multiplier = 4.0
|
for light in model.find_children("*", "Light3D", true, false):
|
||||||
|
light.queue_free()
|
||||||
|
|
||||||
# Body
|
var mat = StandardMaterial3D.new()
|
||||||
var body = MeshInstance3D.new()
|
mat.albedo_texture = load("res://assets/weapons/plasmagun/textures/Plasma_rifle.png")
|
||||||
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)
|
|
||||||
|
|
||||||
# Glow Core
|
for child in model.find_children("*", "MeshInstance3D", true, false):
|
||||||
var core = MeshInstance3D.new()
|
child.set_surface_override_material(0, mat)
|
||||||
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)
|
|
||||||
|
|
||||||
# Muzzle Flash
|
# Muzzle Flash
|
||||||
muzzle_flash = OmniLight3D.new()
|
muzzle_flash = OmniLight3D.new()
|
||||||
|
|||||||
@@ -17,34 +17,16 @@ func _init() -> void:
|
|||||||
projectile_gravity = 0.0 # Flies straight
|
projectile_gravity = 0.0 # Flies straight
|
||||||
|
|
||||||
func _build_model() -> void:
|
func _build_model() -> void:
|
||||||
position = Vector3(0.4, -0.4, -0.6)
|
position = Vector3(0.4, -0.4, -0.945)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
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
|
||||||
muzzle_flash = OmniLight3D.new()
|
muzzle_flash = OmniLight3D.new()
|
||||||
muzzle_flash.light_color = Color(1.0, 0.6, 0.2)
|
muzzle_flash.light_color = Color(1.0, 0.6, 0.2)
|
||||||
|
|||||||
@@ -20,18 +20,19 @@ func _init() -> void:
|
|||||||
projectile_speed = 25.0 # Slower so they have time to home
|
projectile_speed = 25.0 # Slower so they have time to home
|
||||||
|
|
||||||
func _build_model() -> void:
|
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()
|
var model_scene = load("res://assets/weapons/swarm launcher/source/LOD_0.fbx")
|
||||||
mat_box.albedo_color = Color(0.2, 0.4, 0.5)
|
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
|
# Strip lights
|
||||||
var body = MeshInstance3D.new()
|
for child in model.find_children("*", "Light3D", true, false):
|
||||||
var b_mesh = BoxMesh.new()
|
child.queue_free()
|
||||||
b_mesh.size = Vector3(0.2, 0.2, 0.4)
|
|
||||||
b_mesh.material = mat_box
|
|
||||||
body.mesh = b_mesh
|
|
||||||
model_root.add_child(body)
|
|
||||||
|
|
||||||
# Laser
|
# Laser
|
||||||
targeting_laser = MeshInstance3D.new()
|
targeting_laser = MeshInstance3D.new()
|
||||||
@@ -46,7 +47,7 @@ func _build_model() -> void:
|
|||||||
l_mesh.material = l_mat
|
l_mesh.material = l_mat
|
||||||
targeting_laser.mesh = l_mesh
|
targeting_laser.mesh = l_mesh
|
||||||
targeting_laser.rotation.x = deg_to_rad(90)
|
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
|
targeting_laser.visible = false
|
||||||
model_root.add_child(targeting_laser)
|
model_root.add_child(targeting_laser)
|
||||||
|
|
||||||
@@ -123,7 +124,7 @@ func _fire() -> void:
|
|||||||
var tween = create_tween()
|
var tween = create_tween()
|
||||||
tween.tween_property(muzzle_flash, "light_energy", 0.0, 0.1)
|
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
|
var base_dir = -camera.global_transform.basis.z
|
||||||
|
|
||||||
_current_aim_dir = base_dir
|
_current_aim_dir = base_dir
|
||||||
|
|||||||
@@ -58,7 +58,18 @@ func _setup_viewmodel_viewport() -> void:
|
|||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
if is_instance_valid(vm_camera) and is_instance_valid(camera):
|
if is_instance_valid(vm_camera) and is_instance_valid(camera):
|
||||||
vm_camera.global_transform = camera.global_transform
|
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:
|
func _build_loadout() -> void:
|
||||||
var is_auth = false
|
var is_auth = false
|
||||||
|
|||||||