Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db477b4c48 | |||
| 4a5264c5b0 | |||
| ad48f38ca5 | |||
| f0c95dcfd2 | |||
| d060ac449d | |||
| 87060b0b71 | |||
| 638e109d8b | |||
| 4c0e334575 | |||
| e270f34150 | |||
| 20ca43f928 | |||
| aae57c63bd | |||
| 0994a55486 | |||
| 82216bfa64 | |||
| d5098c61e1 | |||
| 194aad8f83 | |||
| 222dcaebb3 | |||
| 0aa29d33e4 | |||
| 6a08487c4c | |||
| f20d532add | |||
| 705b068ed2 | |||
| 3465922be4 | |||
| 46ff83325f | |||
| f6d69545c9 | |||
| 2452aba0d7 | |||
| 3c3251fa7a | |||
| d02b112d99 |
@@ -0,0 +1,248 @@
|
|||||||
|
[gd_scene load_steps=12 format=3]
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; test_range.tscn — Greybox Test Map
|
||||||
|
; =============================================================================
|
||||||
|
; A simple open-area test map for validating movement, shooting, and networking.
|
||||||
|
;
|
||||||
|
; Layout:
|
||||||
|
; - 80×80 open area with flat grey floor
|
||||||
|
; - Low platform (center, 1 unit high, gold)
|
||||||
|
; - Medium platform (east, 2 units high, gold)
|
||||||
|
; - High platform (north-west, 3 units high, red)
|
||||||
|
; - Ramp from ground to low platform (yellow)
|
||||||
|
; - Enclosure/room (east) with doorway for indoor testing (blue-grey walls)
|
||||||
|
; - Spawn point (Marker3D) with group 'spawn_points'
|
||||||
|
; - DirectionalLight3D + WorldEnvironment
|
||||||
|
;
|
||||||
|
; Total geometry: ~132 tris (well under 500 budget)
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="1"]
|
||||||
|
background_mode = 0
|
||||||
|
tonemap_mode = 0
|
||||||
|
glow_enabled = false
|
||||||
|
ambient_light_color = Color(0.25, 0.25, 0.3, 1.0)
|
||||||
|
ambient_light_energy = 0.4
|
||||||
|
ambient_light_sky_contribution = 0.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="2"]
|
||||||
|
albedo_color = Color(0.35, 0.35, 0.35, 1.0)
|
||||||
|
resource_name = "grey_floor"
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="3"]
|
||||||
|
albedo_color = Color(0.8, 0.6, 0.2, 1.0)
|
||||||
|
resource_name = "gold_platform"
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="4"]
|
||||||
|
albedo_color = Color(0.5, 0.5, 0.6, 1.0)
|
||||||
|
resource_name = "bluegrey_wall"
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="5"]
|
||||||
|
albedo_color = Color(0.9, 0.2, 0.2, 1.0)
|
||||||
|
resource_name = "red_high"
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="6"]
|
||||||
|
albedo_color = Color(0.3, 0.4, 0.3, 1.0)
|
||||||
|
resource_name = "green_floor"
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="7"]
|
||||||
|
albedo_color = Color(0.7, 0.7, 0.3, 1.0)
|
||||||
|
resource_name = "yellow_ramp"
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="8"]
|
||||||
|
size = Vector3(8, 4, 4)
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="9"]
|
||||||
|
size = Vector3(8, 4, 4)
|
||||||
|
|
||||||
|
|
||||||
|
; === Root Node ===
|
||||||
|
|
||||||
|
[node name="TestRange" type="Node3D"]
|
||||||
|
|
||||||
|
|
||||||
|
; === Lighting ===
|
||||||
|
|
||||||
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("1")
|
||||||
|
|
||||||
|
[node name="SunLight" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(0.866, 0, -0.5, -0.354, 0.707, -0.612, 0.354, 0.707, 0.612, 0, 8, 0)
|
||||||
|
light_energy = 1.5
|
||||||
|
light_indirect_energy = 0.8
|
||||||
|
light_color = Color(1.0, 0.95, 0.9, 1.0)
|
||||||
|
shadow_enabled = true
|
||||||
|
directional_shadow_max_distance = 60.0
|
||||||
|
directional_shadow_split_1 = 0.1
|
||||||
|
directional_shadow_split_2 = 0.3
|
||||||
|
directional_shadow_split_3 = 0.6
|
||||||
|
directional_shadow_blend_splits = true
|
||||||
|
|
||||||
|
|
||||||
|
; === Spawn Points ===
|
||||||
|
|
||||||
|
[node name="SpawnPoint" type="Marker3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -30, 0.5, 0)
|
||||||
|
groups = ["spawn_points"]
|
||||||
|
|
||||||
|
|
||||||
|
; === CT Spawn Points (Counter-Terrorist) ===
|
||||||
|
|
||||||
|
[node name="CTSpawn_01" type="Marker3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -30, 0.5, -10)
|
||||||
|
groups = ["spawn_points_ct"]
|
||||||
|
|
||||||
|
[node name="CTSpawn_02" type="Marker3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -25, 0.5, -15)
|
||||||
|
groups = ["spawn_points_ct"]
|
||||||
|
|
||||||
|
|
||||||
|
; === T Spawn Points (Terrorist) ===
|
||||||
|
|
||||||
|
[node name="TSpawn_01" type="Marker3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 25, 0.5, 25)
|
||||||
|
groups = ["spawn_points_t"]
|
||||||
|
|
||||||
|
[node name="TSpawn_02" type="Marker3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 30, 0.5, 20)
|
||||||
|
groups = ["spawn_points_t"]
|
||||||
|
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Bomb Sites
|
||||||
|
; =============================================================================
|
||||||
|
; BombsiteA — south-west area, 8×4m zone
|
||||||
|
; BombsiteB — north-east area, 8×4m zone
|
||||||
|
; Each has a CollisionShape3D (BoxShape3D) for overlap detection
|
||||||
|
; and a Marker3D child named "PlantPosition" marking the plant location.
|
||||||
|
|
||||||
|
; --- Bombsite A ---
|
||||||
|
|
||||||
|
|
||||||
|
[node name="BombsiteA" type="Area3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -20, 1.0, -5)
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 0
|
||||||
|
groups = ["bomb_sites"]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="BombsiteA"]
|
||||||
|
shape = SubResource("8")
|
||||||
|
|
||||||
|
[node name="PlantPosition" type="Marker3D" parent="BombsiteA"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0, -1)
|
||||||
|
|
||||||
|
; --- Bombsite B ---
|
||||||
|
|
||||||
|
|
||||||
|
[node name="BombsiteB" type="Area3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15, 1.0, 20)
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 0
|
||||||
|
groups = ["bomb_sites"]
|
||||||
|
|
||||||
|
[node name="PlantPosition" type="Marker3D" parent="BombsiteB"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2)
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="BombsiteB"]
|
||||||
|
shape = SubResource("9")
|
||||||
|
|
||||||
|
|
||||||
|
; === Open Area Floor (80×80) ===
|
||||||
|
|
||||||
|
[node name="Floor" type="CSGBox3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.25, 0)
|
||||||
|
size = Vector3(80, 0.5, 80)
|
||||||
|
material = SubResource("2")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
|
||||||
|
; === Low Platform (center, 1 unit high) ===
|
||||||
|
; 8×8 platform, top at y=2, bottom at y=0, centered at origin
|
||||||
|
|
||||||
|
[node name="LowPlatform" type="CSGBox3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, 0)
|
||||||
|
size = Vector3(8, 2, 8)
|
||||||
|
material = SubResource("3")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
|
||||||
|
; === Medium Platform (east, 2 units high) ===
|
||||||
|
; 6×6 platform, top at y=3, bottom at y=1 — jump-up from low platform height
|
||||||
|
|
||||||
|
[node name="MediumPlatform" type="CSGBox3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15, 2.0, 10)
|
||||||
|
size = Vector3(6, 2, 6)
|
||||||
|
material = SubResource("3")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
|
||||||
|
; === High Platform (north-west, 3 units high) ===
|
||||||
|
; 3×3 small platform, top at y=4 — requires sprint-jump to reach
|
||||||
|
|
||||||
|
[node name="HighPlatform" type="CSGBox3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -20, 3.0, -15)
|
||||||
|
size = Vector3(3, 2, 3)
|
||||||
|
material = SubResource("5")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
|
||||||
|
; === Ramp (south of center, leads ground → low platform top) ===
|
||||||
|
; Tilted ~20° around X axis, 6 units long along Z.
|
||||||
|
; South end near ground, north end meets low platform top (y=2).
|
||||||
|
|
||||||
|
[node name="Ramp" type="CSGBox3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.940, -0.342, 0, 0.342, 0.940, 0, 1.0, 5)
|
||||||
|
size = Vector3(6, 0.2, 6)
|
||||||
|
material = SubResource("7")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; Enclosure — Indoor Testing Room
|
||||||
|
; =============================================================================
|
||||||
|
; 12×12 interior room, 4 units tall, with a 2-unit doorway on the south face.
|
||||||
|
; Centered at (30, 0, 30).
|
||||||
|
|
||||||
|
[node name="Enclosure" type="Node3D" parent="."]
|
||||||
|
|
||||||
|
[node name="RoomFloor" type="CSGBox3D" parent="Enclosure"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 30, 0.25, 30)
|
||||||
|
size = Vector3(12, 0.5, 12)
|
||||||
|
material = SubResource("6")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
; North wall (z = 24)
|
||||||
|
[node name="Wall_N" type="CSGBox3D" parent="Enclosure"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 30, 2.0, 24.25)
|
||||||
|
size = Vector3(12, 4, 0.5)
|
||||||
|
material = SubResource("4")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
; South wall left segment (doorway gap from x=29 to x=31)
|
||||||
|
[node name="Wall_S_Left" type="CSGBox3D" parent="Enclosure"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 26.5, 2.0, 35.75)
|
||||||
|
size = Vector3(5, 4, 0.5)
|
||||||
|
material = SubResource("4")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
; South wall right segment
|
||||||
|
[node name="Wall_S_Right" type="CSGBox3D" parent="Enclosure"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 33.5, 2.0, 35.75)
|
||||||
|
size = Vector3(5, 4, 0.5)
|
||||||
|
material = SubResource("4")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
; West wall (x = 24)
|
||||||
|
[node name="Wall_W" type="CSGBox3D" parent="Enclosure"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 24.25, 2.0, 30)
|
||||||
|
size = Vector3(0.5, 4, 12)
|
||||||
|
material = SubResource("4")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
; East wall (x = 36)
|
||||||
|
[node name="Wall_E" type="CSGBox3D" parent="Enclosure"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 35.75, 2.0, 30)
|
||||||
|
size = Vector3(0.5, 4, 12)
|
||||||
|
material = SubResource("4")
|
||||||
|
use_collision = true
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
@tool
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# art_kit.gd — Modular Art Kit Factory
|
||||||
|
# =============================================================================
|
||||||
|
# Provides factory methods for spawning modular environment pieces and a
|
||||||
|
# preview catalogue for map builders.
|
||||||
|
#
|
||||||
|
# Each piece is a standalone .tscn under pieces/ with:
|
||||||
|
# - CSG shapes (Box3D, Cylinder3D, Polygon3D, Combiner3D)
|
||||||
|
# - Solid-color StandardMaterial3D placeholders (roughness 0.8, metallic 0.0)
|
||||||
|
# - Collision enabled (use_collision = true or StaticBody3D + CollisionShape3D)
|
||||||
|
# - < 200 tris each
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# var piece = ArtKit.spawn_piece("crate", parent_node)
|
||||||
|
# var cat = ArtKit.preview_catalogue()
|
||||||
|
# var stack = ArtKit.random_crate_layout(3, 2) # returns parent Node3D
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
const PIECES_DIR := "res://client/assets/scenes/modular/pieces/"
|
||||||
|
|
||||||
|
# Catalogue definition: name, description, filename (without .tscn)
|
||||||
|
const CATALOGUE: Array[Dictionary] = [
|
||||||
|
{
|
||||||
|
"name": "floor_tile",
|
||||||
|
"description": "2×2×0.2m concrete slab — grey",
|
||||||
|
"file": "floor_tile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wall_segment",
|
||||||
|
"description": "4×0.2×3m wall panel — blue-grey",
|
||||||
|
"file": "wall_segment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "corner_wall",
|
||||||
|
"description": "L-shaped corner piece (2m each leg) — blue-grey",
|
||||||
|
"file": "corner_wall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pillar",
|
||||||
|
"description": "0.3×3×0.3m column — dark grey",
|
||||||
|
"file": "pillar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "crate",
|
||||||
|
"description": "1×1×1m storage box — brown/tan",
|
||||||
|
"file": "crate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "barrel",
|
||||||
|
"description": "0.6m radius × 1m tall cylinder — red",
|
||||||
|
"file": "barrel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cover_wall",
|
||||||
|
"description": "2×0.2×1.3m low wall (crouch-height cover) — grey",
|
||||||
|
"file": "cover_wall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ramp",
|
||||||
|
"description": "2×2m, 30° incline ramp — yellow",
|
||||||
|
"file": "ramp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stairs",
|
||||||
|
"description": "3-step staircase (2m wide, 0.25m rise per step) — grey",
|
||||||
|
"file": "stairs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "window_frame",
|
||||||
|
"description": "4×3m wall with 2×2m opening — blue-grey",
|
||||||
|
"file": "window_frame"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public API
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Spawns a piece by name, adds it as a child of `parent`, and returns it.
|
||||||
|
## Returns null if the piece name is unknown or the scene fails to load.
|
||||||
|
static func spawn_piece(piece_name: String, parent: Node) -> Node3D:
|
||||||
|
var entry := _find_entry(piece_name)
|
||||||
|
if entry.is_empty():
|
||||||
|
push_error("ArtKit: Unknown piece '%s'. Use preview_catalogue() to see available pieces." % piece_name)
|
||||||
|
return null
|
||||||
|
|
||||||
|
var path := PIECES_DIR + entry.file + ".tscn"
|
||||||
|
var scene := load(path)
|
||||||
|
if scene == null:
|
||||||
|
push_error("ArtKit: Failed to load scene at '%s'." % path)
|
||||||
|
return null
|
||||||
|
|
||||||
|
var instance: Node3D = scene.instantiate()
|
||||||
|
parent.add_child(instance)
|
||||||
|
instance.owner = parent if not Engine.is_editor_hint() else _get_editor_owner(parent)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
## Returns an array of { name, description, path } for all available pieces.
|
||||||
|
static func preview_catalogue() -> Array[Dictionary]:
|
||||||
|
var result: Array[Dictionary] = []
|
||||||
|
for entry in CATALOGUE:
|
||||||
|
result.append({
|
||||||
|
"name": entry.name,
|
||||||
|
"description": entry.description,
|
||||||
|
"path": PIECES_DIR + entry.file + ".tscn"
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
## Generates a random stack of crates for quick testing layouts.
|
||||||
|
## `width` — number of crates along X (default 3)
|
||||||
|
## `height` — number of crates stacked vertically (default 2)
|
||||||
|
## Returns a Node3D containing the crate instances.
|
||||||
|
static func random_crate_layout(width: int = 3, height: int = 2) -> Node3D:
|
||||||
|
var root := Node3D.new()
|
||||||
|
root.name = "CrateLayout_%dx%d" % [width, height]
|
||||||
|
|
||||||
|
var crate_scene := load(PIECES_DIR + "crate.tscn")
|
||||||
|
if crate_scene == null:
|
||||||
|
push_error("ArtKit: Could not load crate scene.")
|
||||||
|
return root
|
||||||
|
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
# Add slight random offset for natural look (±0.05m)
|
||||||
|
var offset_x := randf_range(-0.05, 0.05)
|
||||||
|
var offset_z := randf_range(-0.05, 0.05)
|
||||||
|
# Full rotation in 90° increments
|
||||||
|
var rot_y := randi() % 4 * 90.0
|
||||||
|
|
||||||
|
var crate: Node3D = crate_scene.instantiate()
|
||||||
|
crate.position = Vector3(x * 1.0 + offset_x, y * 1.0 + 0.5, offset_z)
|
||||||
|
crate.rotation_degrees = Vector3(0, rot_y, 0)
|
||||||
|
root.add_child(crate)
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static func _find_entry(piece_name: String) -> Dictionary:
|
||||||
|
for entry in CATALOGUE:
|
||||||
|
if entry.name == piece_name:
|
||||||
|
return entry
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
## Returns the editor's top-level owner so new nodes are treated as part of the scene.
|
||||||
|
static func _get_editor_owner(node: Node) -> Node:
|
||||||
|
var owner = node
|
||||||
|
while owner and owner.owner:
|
||||||
|
owner = owner.owner
|
||||||
|
return owner
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dnopn0wj58j83
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.7, 0.15, 0.1, 1.0)
|
||||||
|
roughness = 0.6
|
||||||
|
metallic = 0.2
|
||||||
|
resource_name = "red_barrel"
|
||||||
|
|
||||||
|
[node name="Barrel" type="CSGCylinder3D"]
|
||||||
|
radius = 0.6
|
||||||
|
height = 1.0
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
slices = 16
|
||||||
|
visibility_range_end = 30.0
|
||||||
|
visibility_range_end_margin = 3.0
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.35, 0.4, 0.5, 1.0)
|
||||||
|
roughness = 0.8
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "bluegrey_corner"
|
||||||
|
|
||||||
|
[node name="CornerWall" type="CSGCombiner3D"]
|
||||||
|
use_collision = false
|
||||||
|
visibility_range_end = 50.0
|
||||||
|
visibility_range_end_margin = 5.0
|
||||||
|
|
||||||
|
[node name="WallA" type="CSGBox3D" parent="CornerWall"]
|
||||||
|
operation = 0
|
||||||
|
size = Vector3(2, 3, 0.2)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.1)
|
||||||
|
|
||||||
|
[node name="WallB" type="CSGBox3D" parent="CornerWall"]
|
||||||
|
operation = 0
|
||||||
|
size = Vector3(0.2, 3, 2)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.1, 0, 0)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.4, 0.4, 0.4, 1.0)
|
||||||
|
roughness = 0.8
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "grey_cover"
|
||||||
|
|
||||||
|
[node name="CoverWall" type="CSGBox3D"]
|
||||||
|
size = Vector3(2, 1.3, 0.2)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
visibility_range_end = 40.0
|
||||||
|
visibility_range_end_margin = 5.0
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.55, 0.35, 0.15, 1.0)
|
||||||
|
roughness = 0.85
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "brown_crate"
|
||||||
|
|
||||||
|
[node name="Crate" type="CSGBox3D"]
|
||||||
|
size = Vector3(1, 1, 1)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
visibility_range_end = 40.0
|
||||||
|
visibility_range_end_margin = 5.0
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.5, 0.5, 0.5, 1.0)
|
||||||
|
roughness = 0.8
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "concrete_grey"
|
||||||
|
|
||||||
|
[node name="FloorTile" type="CSGBox3D"]
|
||||||
|
size = Vector3(2, 0.2, 2)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
visibility_range_end = 60.0
|
||||||
|
visibility_range_end_margin = 10.0
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.2, 0.2, 0.22, 1.0)
|
||||||
|
roughness = 0.8
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "darkgrey_pillar"
|
||||||
|
|
||||||
|
[node name="Pillar" type="CSGBox3D"]
|
||||||
|
size = Vector3(0.3, 3, 0.3)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
visibility_range_end = 30.0
|
||||||
|
visibility_range_end_margin = 3.0
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.7, 0.7, 0.3, 1.0)
|
||||||
|
roughness = 0.8
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "yellow_ramp"
|
||||||
|
|
||||||
|
[node name="Ramp" type="CSGPolygon3D"]
|
||||||
|
polygon = PackedVector2Array(0, 0, 2, 0, 2, 1.155)
|
||||||
|
depth = 2.0
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
smooth_faces = true
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.45, 0.45, 0.45, 1.0)
|
||||||
|
roughness = 0.8
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "grey_stairs"
|
||||||
|
|
||||||
|
[node name="Stairs" type="CSGCombiner3D"]
|
||||||
|
use_collision = false
|
||||||
|
visibility_range_end = 40.0
|
||||||
|
visibility_range_end_margin = 5.0
|
||||||
|
|
||||||
|
[node name="Step1" type="CSGBox3D" parent="Stairs"]
|
||||||
|
operation = 0
|
||||||
|
size = Vector3(2, 0.25, 0.5)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.125, 0.75)
|
||||||
|
|
||||||
|
[node name="Step2" type="CSGBox3D" parent="Stairs"]
|
||||||
|
operation = 0
|
||||||
|
size = Vector3(2, 0.25, 0.5)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.375, 0.25)
|
||||||
|
|
||||||
|
[node name="Step3" type="CSGBox3D" parent="Stairs"]
|
||||||
|
operation = 0
|
||||||
|
size = Vector3(2, 0.25, 0.5)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.625, -0.25)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.35, 0.4, 0.5, 1.0)
|
||||||
|
roughness = 0.8
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "bluegrey_wall"
|
||||||
|
|
||||||
|
[node name="WallSegment" type="CSGBox3D"]
|
||||||
|
size = Vector3(4, 3, 0.2)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
visibility_range_end = 50.0
|
||||||
|
visibility_range_end_margin = 5.0
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="1"]
|
||||||
|
albedo_color = Color(0.35, 0.4, 0.5, 1.0)
|
||||||
|
roughness = 0.8
|
||||||
|
metallic = 0.0
|
||||||
|
resource_name = "bluegrey_window"
|
||||||
|
|
||||||
|
[node name="WindowFrame" type="CSGCombiner3D"]
|
||||||
|
use_collision = false
|
||||||
|
visibility_range_end = 40.0
|
||||||
|
visibility_range_end_margin = 5.0
|
||||||
|
|
||||||
|
[node name="MainWall" type="CSGBox3D" parent="WindowFrame"]
|
||||||
|
operation = 0
|
||||||
|
size = Vector3(4, 3, 0.2)
|
||||||
|
material = SubResource("1")
|
||||||
|
use_collision = true
|
||||||
|
|
||||||
|
[node name="WindowCutout" type="CSGBox3D" parent="WindowFrame"]
|
||||||
|
operation = 1
|
||||||
|
size = Vector3(2, 2, 0.25)
|
||||||
|
use_collision = true
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||||
@@ -88,6 +88,12 @@ var _input_sequence: int = 0
|
|||||||
## Cached input dictionary (avoids alloc per frame).
|
## Cached input dictionary (avoids alloc per frame).
|
||||||
var _input_dict: Dictionary = {}
|
var _input_dict: Dictionary = {}
|
||||||
|
|
||||||
|
## Prediction controller reference (set by ClientPrediction system).
|
||||||
|
## When non-null and prediction_enabled, the controller uses client-side
|
||||||
|
## prediction with local movement for instant feedback instead of
|
||||||
|
## reading position from the SimulationServer entity directly.
|
||||||
|
var _prediction: Node = null
|
||||||
|
|
||||||
## Mouse capture state.
|
## Mouse capture state.
|
||||||
var _mouse_captured: bool = false
|
var _mouse_captured: bool = false
|
||||||
var _mouse_clicked_this_frame: bool = false
|
var _mouse_clicked_this_frame: bool = false
|
||||||
@@ -193,6 +199,12 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
|
# PREDICTION HOOK: capture pre-input state snapshot.
|
||||||
|
# Must be called BEFORE any input processing or movement so the
|
||||||
|
# snapshot represents the state at the start of this tick.
|
||||||
|
if _prediction and _prediction.prediction_enabled:
|
||||||
|
_prediction.on_before_tick()
|
||||||
|
|
||||||
# 1. Capture input state
|
# 1. Capture input state
|
||||||
var input_dir := _get_move_direction()
|
var input_dir := _get_move_direction()
|
||||||
var jump_pressed := Input.is_action_just_pressed(&"jump")
|
var jump_pressed := Input.is_action_just_pressed(&"jump")
|
||||||
@@ -249,19 +261,24 @@ func _physics_process(delta: float) -> void:
|
|||||||
if should_fire:
|
if should_fire:
|
||||||
_server.fire_weapon(entity_id)
|
_server.fire_weapon(entity_id)
|
||||||
|
|
||||||
# 6. Send input to simulation server
|
# 6. Build input dictionary (all modes — mutate cached dict to avoid alloc)
|
||||||
if _server != null and entity_id >= 0:
|
_input_dict["move_direction"] = input_dir
|
||||||
# Build input for SimulationServer (mutate cached dict to avoid alloc)
|
_input_dict["look_yaw"] = rad_to_deg(_yaw)
|
||||||
_input_dict["move_direction"] = input_dir
|
_input_dict["look_pitch"] = rad_to_deg(_pitch)
|
||||||
_input_dict["look_yaw"] = rad_to_deg(_yaw)
|
_input_dict["jump"] = jump_pressed
|
||||||
_input_dict["look_pitch"] = rad_to_deg(_pitch)
|
_input_dict["crouch"] = _crouch_active
|
||||||
_input_dict["jump"] = jump_pressed
|
_input_dict["sprint"] = _sprint_active
|
||||||
_input_dict["crouch"] = _crouch_active
|
_input_dict["shoot"] = should_fire
|
||||||
_input_dict["sprint"] = _sprint_active
|
_input_dict["aim"] = aim_pressed
|
||||||
_input_dict["shoot"] = should_fire
|
_input_dict["input_sequence"] = _input_sequence
|
||||||
_input_dict["aim"] = aim_pressed
|
|
||||||
_input_dict["input_sequence"] = _input_sequence
|
|
||||||
|
|
||||||
|
# 6b. Route input to appropriate handler.
|
||||||
|
if _prediction and _prediction.prediction_enabled:
|
||||||
|
# Client prediction: input sending is handled by the prediction
|
||||||
|
# controller's on_after_tick() call at the end of this function.
|
||||||
|
pass
|
||||||
|
elif _server != null and entity_id >= 0:
|
||||||
|
# Listen server: send to local SimulationServer.
|
||||||
_server.apply_input(entity_id, _input_dict)
|
_server.apply_input(entity_id, _input_dict)
|
||||||
|
|
||||||
# Check for hit feedback from last tick
|
# Check for hit feedback from last tick
|
||||||
@@ -271,16 +288,20 @@ func _physics_process(delta: float) -> void:
|
|||||||
var hit_pos := Vector3.ZERO # approximate — we don't have exact world hit position from server yet
|
var hit_pos := Vector3.ZERO # approximate — we don't have exact world hit position from server yet
|
||||||
_weapon.on_hit_confirmed(hit_pos, hit_result.get("damage", 0.0), hit_result.get("killed", false))
|
_weapon.on_hit_confirmed(hit_pos, hit_result.get("damage", 0.0), hit_result.get("killed", false))
|
||||||
|
|
||||||
_input_sequence += 1
|
_input_sequence += 1
|
||||||
|
|
||||||
# 7. Read server state back (for local simulation / listen server)
|
# 7. Update position.
|
||||||
# The SimulationServer.tick() call happens in the game manager / network layer.
|
# Architecture:
|
||||||
# Here we optionally read the entity's canonical position from the server.
|
# - Client prediction: do local CharacterBody3D movement (instant feedback),
|
||||||
# NOTE: On a dedicated client, position comes from the network replication
|
# the prediction controller handles reconciliation when authoritative
|
||||||
# layer (server snapshot), not from direct server entity access.
|
# server state arrives.
|
||||||
# On a listen server (host+client same process) or in standalone mode,
|
# - Listen server: read authoritative position from SimulationServer entity.
|
||||||
# reading position directly from the server entity is correct.
|
# - Standalone (no server): fallback CharacterBody3D movement.
|
||||||
if _server != null and entity_id >= 0:
|
if _prediction and _prediction.prediction_enabled:
|
||||||
|
# Client prediction path — local movement for instant feedback.
|
||||||
|
_move_local(input_dir, delta, jump_pressed)
|
||||||
|
elif _server != null and entity_id >= 0:
|
||||||
|
# Listen server: read from SimulationServer entity.
|
||||||
var entity = _server.get_entity(entity_id)
|
var entity = _server.get_entity(entity_id)
|
||||||
if entity != null and entity.is_alive():
|
if entity != null and entity.is_alive():
|
||||||
# Server position is ground truth — apply it to the visual body
|
# Server position is ground truth — apply it to the visual body
|
||||||
@@ -290,6 +311,12 @@ func _physics_process(delta: float) -> void:
|
|||||||
# Standalone mode: do local CharacterBody3D physics
|
# Standalone mode: do local CharacterBody3D physics
|
||||||
_move_local(input_dir, delta, jump_pressed)
|
_move_local(input_dir, delta, jump_pressed)
|
||||||
|
|
||||||
|
# PREDICTION HOOK: post-tick — send input to server via RPC.
|
||||||
|
# Must be called AFTER local movement so the prediction controller
|
||||||
|
# can send the input that produced this tick's movement.
|
||||||
|
if _prediction and _prediction.prediction_enabled:
|
||||||
|
_prediction.on_after_tick(_input_dict)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Local movement (standalone / no-server fallback)
|
# Local movement (standalone / no-server fallback)
|
||||||
@@ -395,6 +422,13 @@ func set_entity_id(id: int) -> void:
|
|||||||
entity_id = id
|
entity_id = id
|
||||||
|
|
||||||
|
|
||||||
|
## Set the prediction controller for client-side prediction/reconciliation.
|
||||||
|
## The prediction node must have on_before_tick() / on_after_tick() methods.
|
||||||
|
## Typically a ClientPrediction instance added as a child or sibling.
|
||||||
|
func set_prediction(prediction_node: Node) -> void:
|
||||||
|
_prediction = prediction_node
|
||||||
|
|
||||||
|
|
||||||
## Get current crouch amount (0.0 = standing, 1.0 = fully crouched).
|
## Get current crouch amount (0.0 = standing, 1.0 = fully crouched).
|
||||||
func get_crouch_amount() -> float:
|
func get_crouch_amount() -> float:
|
||||||
return _crouch_current
|
return _crouch_current
|
||||||
|
|||||||
@@ -0,0 +1,293 @@
|
|||||||
|
## BombCarrier — Client-side bomb status UI and prompts.
|
||||||
|
##
|
||||||
|
## Attached to the local T player who carries the bomb.
|
||||||
|
## Provides visual feedback for:
|
||||||
|
## - Bomb status (planted/defusing/timer)
|
||||||
|
## - Direction indicator to planted bomb
|
||||||
|
## - "Hold E to defuse" prompt (CT, near bomb)
|
||||||
|
## - "Hold E to plant" prompt (T, in bombsite, LIVE phase)
|
||||||
|
##
|
||||||
|
## This script is a Node that should be added as a child of the local
|
||||||
|
## player's character node (FPSCharacterController) on the client side.
|
||||||
|
## It communicates with the server BombObjective via RPC or state sync.
|
||||||
|
|
||||||
|
extends Node
|
||||||
|
class_name BombCarrier
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Signals
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Emitted when the nearby interaction prompt changes.
|
||||||
|
signal prompt_changed(prompt_text: String, visible: bool)
|
||||||
|
|
||||||
|
## Emitted when bomb status UI should update.
|
||||||
|
signal bomb_status_updated(planted: bool, timer: float, defusing: bool, defuse_progress: float)
|
||||||
|
|
||||||
|
## Emitted when the bomb direction indicator should update.
|
||||||
|
signal bomb_direction_updated(bearing_degrees: float, distance: float)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Exports
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## The "Use/Interact" action name (matched in Input Map).
|
||||||
|
@export var interact_action: String = "interact"
|
||||||
|
|
||||||
|
## Interaction distance for planting (in Godot units).
|
||||||
|
@export var plant_distance: float = 2.0
|
||||||
|
|
||||||
|
## Interaction distance for defusing (in Godot units).
|
||||||
|
@export var defuse_distance: float = 3.0
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# State
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Whether this player has the bomb (T-side bomb carrier).
|
||||||
|
var has_bomb: bool = false
|
||||||
|
|
||||||
|
## Whether the bomb is currently planted.
|
||||||
|
var _bomb_planted: bool = false
|
||||||
|
|
||||||
|
## Position of the planted bomb (server-authoritative).
|
||||||
|
var _bomb_position: Vector3 = Vector3.ZERO
|
||||||
|
|
||||||
|
## Remaining bomb timer (received from server).
|
||||||
|
var _bomb_timer: float = 0.0
|
||||||
|
|
||||||
|
## Whether the local player is currently defusing.
|
||||||
|
var _is_defusing: bool = false
|
||||||
|
|
||||||
|
## Current defuse progress (0.0 to 1.0).
|
||||||
|
var _defuse_progress: float = 0.0
|
||||||
|
|
||||||
|
## Current prompt text to show.
|
||||||
|
var _current_prompt: String = ""
|
||||||
|
|
||||||
|
## Whether the prompt is currently visible.
|
||||||
|
var _prompt_visible: bool = false
|
||||||
|
|
||||||
|
## Whether we are inside a bombsite.
|
||||||
|
var _in_bombsite: bool = false
|
||||||
|
|
||||||
|
## Whether we are near the planted bomb.
|
||||||
|
var _near_planted_bomb: bool = false
|
||||||
|
|
||||||
|
## Whether the interact key is being held.
|
||||||
|
var _interact_held: bool = false
|
||||||
|
|
||||||
|
## Hold-down timer for plant/defuse (E key held long enough).
|
||||||
|
var _interact_hold_time: float = 0.0
|
||||||
|
|
||||||
|
## Time required to hold E to start action (for UX consistency).
|
||||||
|
var _interact_hold_duration: float = 0.0 # Immediate on press
|
||||||
|
|
||||||
|
## Reference to the local player's parent/character controller.
|
||||||
|
var _character_controller: Node = null
|
||||||
|
|
||||||
|
## Reference to the bomb objective on the server (for RPC calls).
|
||||||
|
## Set by the game manager after instantiation.
|
||||||
|
var bomb_objective_path: NodePath = NodePath()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Lifecycle
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_character_controller = get_parent()
|
||||||
|
if _character_controller == null:
|
||||||
|
push_warning("[BombCarrier] No parent node — prompts will not work")
|
||||||
|
|
||||||
|
# Try to set up the interact action if it doesn't exist
|
||||||
|
if not InputMap.has_action(interact_action):
|
||||||
|
var event := InputEventKey.new()
|
||||||
|
event.keycode = KEY_E
|
||||||
|
InputMap.add_action(interact_action)
|
||||||
|
InputMap.action_add_event(interact_action, event)
|
||||||
|
|
||||||
|
print("[BombCarrier] Initialised — has_bomb=%s" % has_bomb)
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
_update_interaction(delta)
|
||||||
|
_emit_status_updates()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public API — Server state sync
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Called by the game manager or network sync to update bomb state.
|
||||||
|
func sync_bomb_state(planted: bool, position: Vector3, timer: float,
|
||||||
|
defusing: bool, defuse_progress: float) -> void:
|
||||||
|
_bomb_planted = planted
|
||||||
|
_bomb_position = position
|
||||||
|
_bomb_timer = timer
|
||||||
|
_is_defusing = defusing
|
||||||
|
_defuse_progress = defuse_progress
|
||||||
|
|
||||||
|
## Set whether this player has the bomb (assigned by server).
|
||||||
|
func set_has_bomb(value: bool) -> void:
|
||||||
|
has_bomb = value
|
||||||
|
print("[BombCarrier] Has bomb: %s" % value)
|
||||||
|
|
||||||
|
## Set whether the local player is inside a bombsite Area3D.
|
||||||
|
## Called by the bombsite Area3D signal handlers on the client.
|
||||||
|
func set_in_bombsite(value: bool) -> void:
|
||||||
|
_in_bombsite = value
|
||||||
|
|
||||||
|
## Set whether the local player is near the planted bomb.
|
||||||
|
## Called by the bomb area proximity check.
|
||||||
|
func set_near_planted_bomb(value: bool) -> void:
|
||||||
|
_near_planted_bomb = value
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public API — RPC calls to server
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Request to plant the bomb at the current position.
|
||||||
|
## Called when the T player presses E inside a bombsite with the bomb.
|
||||||
|
func request_plant() -> void:
|
||||||
|
if not has_bomb or not _in_bombsite:
|
||||||
|
return
|
||||||
|
if _bomb_planted:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Send plant request to server
|
||||||
|
# In real implementation, this would be an RPC call:
|
||||||
|
# _send_plant_request.rpc_id(1, _character_controller.global_position)
|
||||||
|
_send_plant_request(_character_controller.global_position)
|
||||||
|
|
||||||
|
## Request to start defusing the bomb.
|
||||||
|
## Called when the CT player presses E near the planted bomb.
|
||||||
|
func request_defuse() -> void:
|
||||||
|
if not _near_planted_bomb or not _bomb_planted:
|
||||||
|
return
|
||||||
|
if _is_defusing:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Send defuse request to server
|
||||||
|
# _send_defuse_request.rpc_id(1)
|
||||||
|
_send_defuse_request()
|
||||||
|
|
||||||
|
## Request to cancel defuse (when player moves or is interrupted).
|
||||||
|
func request_cancel_defuse() -> void:
|
||||||
|
if not _is_defusing:
|
||||||
|
return
|
||||||
|
_send_cancel_defuse()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# RPC stubs (to be replaced with actual RPC calls)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _send_plant_request(position: Vector3) -> void:
|
||||||
|
# Placeholder: in real implementation, send RPC to server
|
||||||
|
print("[BombCarrier] Plant request at (%.1f, %.1f, %.1f)" %
|
||||||
|
[position.x, position.y, position.z])
|
||||||
|
|
||||||
|
func _send_defuse_request() -> void:
|
||||||
|
# Placeholder: in real implementation, send RPC to server
|
||||||
|
print("[BombCarrier] Defuse request")
|
||||||
|
|
||||||
|
func _send_cancel_defuse() -> void:
|
||||||
|
# Placeholder: in real implementation, send RPC to server
|
||||||
|
print("[BombCarrier] Cancel defuse request")
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal — Interaction logic
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _update_interaction(delta: float) -> void:
|
||||||
|
var interact_pressed: bool = Input.is_action_pressed(interact_action)
|
||||||
|
var prompt_text: String = ""
|
||||||
|
var show_prompt: bool = false
|
||||||
|
|
||||||
|
# Determine what prompt to show
|
||||||
|
if _bomb_planted and _near_planted_bomb and not _is_defusing:
|
||||||
|
# CT player near planted bomb — defuse prompt
|
||||||
|
var team: int = _get_local_player_team()
|
||||||
|
if team == TeamData.Team.COUNTER_TERRORIST:
|
||||||
|
prompt_text = "Hold E to defuse"
|
||||||
|
show_prompt = true
|
||||||
|
|
||||||
|
elif has_bomb and _in_bombsite and not _bomb_planted:
|
||||||
|
# T player with bomb inside bombsite — plant prompt
|
||||||
|
if _is_round_live():
|
||||||
|
prompt_text = "Hold E to plant"
|
||||||
|
show_prompt = true
|
||||||
|
|
||||||
|
elif _bomb_planted and _is_defusing:
|
||||||
|
# Currently defusing — show progress
|
||||||
|
var progress_pct: int = int(_defuse_progress * 100.0)
|
||||||
|
prompt_text = "Defusing... %d%%" % progress_pct
|
||||||
|
show_prompt = true
|
||||||
|
|
||||||
|
# Handle interact hold
|
||||||
|
if interact_pressed and show_prompt:
|
||||||
|
if not _interact_held:
|
||||||
|
_interact_held = true
|
||||||
|
_interact_hold_time = 0.0
|
||||||
|
|
||||||
|
_interact_hold_time += delta
|
||||||
|
|
||||||
|
# Check if we should trigger the action
|
||||||
|
if _interact_hold_time >= _interact_hold_duration:
|
||||||
|
_interact_hold_time = 0.0
|
||||||
|
if prompt_text.contains("defuse"):
|
||||||
|
request_defuse()
|
||||||
|
elif prompt_text.contains("plant"):
|
||||||
|
request_plant()
|
||||||
|
else:
|
||||||
|
# Cancel any pending interaction
|
||||||
|
if _interact_held:
|
||||||
|
_interact_held = false
|
||||||
|
_interact_hold_time = 0.0
|
||||||
|
# If we were defusing and released, cancel
|
||||||
|
if _is_defusing:
|
||||||
|
request_cancel_defuse()
|
||||||
|
|
||||||
|
# Update prompt if changed
|
||||||
|
if prompt_text != _current_prompt or show_prompt != _prompt_visible:
|
||||||
|
_current_prompt = prompt_text
|
||||||
|
_prompt_visible = show_prompt
|
||||||
|
prompt_changed.emit(prompt_text, show_prompt)
|
||||||
|
|
||||||
|
func _emit_status_updates() -> void:
|
||||||
|
# Emit bomb status updates (rate-limited is handled by engine)
|
||||||
|
bomb_status_updated.emit(_bomb_planted, _bomb_timer, _is_defusing, _defuse_progress)
|
||||||
|
|
||||||
|
# Emit direction indicator if bomb is planted
|
||||||
|
if _bomb_planted and _character_controller != null:
|
||||||
|
var player_pos: Vector3 = _character_controller.global_position
|
||||||
|
var direction: Vector3 = _bomb_position - player_pos
|
||||||
|
var distance: float = direction.length()
|
||||||
|
if distance > 0.01:
|
||||||
|
var bearing: float = atan2(direction.x, -direction.z) # Relative to forward (Z)
|
||||||
|
bomb_direction_updated.emit(rad_to_deg(bearing), distance)
|
||||||
|
else:
|
||||||
|
bomb_direction_updated.emit(0.0, 0.0)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal — Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Get the local player's team (from TeamManager singleton if available).
|
||||||
|
func _get_local_player_team() -> int:
|
||||||
|
var team_manager: TeamManager = get_node_or_null("/root/TeamManager")
|
||||||
|
if team_manager:
|
||||||
|
var peer_id: int = multiplayer.get_unique_id() if multiplayer.has_method(&\"get_unique_id\") else -1
|
||||||
|
if peer_id >= 0:
|
||||||
|
return team_manager.get_player_team(peer_id)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
## Check if the current round is in LIVE phase.
|
||||||
|
func _is_round_live() -> bool:
|
||||||
|
var round_manager: RoundManager = get_node_or_null("/root/RoundManager")
|
||||||
|
if round_manager:
|
||||||
|
return round_manager.get_phase() == RoundManager.RoundPhase.LIVE
|
||||||
|
return false
|
||||||
|
|
||||||
|
## Get the multiplayer API (stub for compatibility).
|
||||||
|
func get_multiplayer() -> MultiplayerAPI:
|
||||||
|
if multiplayer and multiplayer.has_method(&\"get_unique_id\"):
|
||||||
|
return multiplayer
|
||||||
|
return null
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dtvf72mhgdat7
|
||||||
@@ -0,0 +1,445 @@
|
|||||||
|
## BuyMenu — Client-side buy menu UI for weapon and equipment purchases.
|
||||||
|
##
|
||||||
|
## Displays a grid of purchasable weapons with name, cost, and key stats.
|
||||||
|
## Highlights weapons the player can afford, shows current money at the top,
|
||||||
|
## and supports both keyboard (numeric keys) and mouse-click selection.
|
||||||
|
##
|
||||||
|
## Opens during the PREP (buy) phase when the player is inside a buy zone.
|
||||||
|
## Automatically closes when the buy phase ends or the player leaves the zone.
|
||||||
|
##
|
||||||
|
## Usage (add as child of the player's UI root or HUD):
|
||||||
|
## var buy_menu = preload("res://client/characters/weapon/buy_menu.gd").new()
|
||||||
|
## add_child(buy_menu)
|
||||||
|
##
|
||||||
|
## # Show it
|
||||||
|
## buy_menu.open()
|
||||||
|
## buy_menu.populate(affordable_weapons_list, current_money)
|
||||||
|
##
|
||||||
|
## Signals:
|
||||||
|
## buy_menu_opened()
|
||||||
|
## buy_menu_closed()
|
||||||
|
## purchase_completed(weapon_id)
|
||||||
|
## purchase_failed(reason: String)
|
||||||
|
##
|
||||||
|
class_name BuyMenu
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Signals
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
## Emitted when the buy menu opens.
|
||||||
|
signal buy_menu_opened()
|
||||||
|
|
||||||
|
## Emitted when the buy menu closes.
|
||||||
|
signal buy_menu_closed()
|
||||||
|
|
||||||
|
## Emitted when a purchase is confirmed by the server.
|
||||||
|
signal purchase_completed(weapon_id: String)
|
||||||
|
|
||||||
|
## Emitted when a purchase is denied by the server.
|
||||||
|
signal purchase_failed(reason: String)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Exports — Layout
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
## Background panel colour.
|
||||||
|
@export var panel_color: Color = Color(0.1, 0.1, 0.15, 0.92)
|
||||||
|
|
||||||
|
## Accent colour for headers and affordable items.
|
||||||
|
@export var accent_color: Color = Color(0.2, 0.7, 0.3, 1.0)
|
||||||
|
|
||||||
|
## Colour for items the player cannot afford.
|
||||||
|
@export var unaffordable_color: Color = Color(0.5, 0.5, 0.5, 1.0)
|
||||||
|
|
||||||
|
## Text colour.
|
||||||
|
@export var text_color: Color = Color(0.95, 0.95, 0.95, 1.0)
|
||||||
|
|
||||||
|
## Key binding for opening the buy menu (default: B).
|
||||||
|
@export var open_key: Key = KEY_B
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal state
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
## Reference to the BuyMenuHandler on the server (for sending RPCs).
|
||||||
|
## Injected by the UI manager or found at runtime.
|
||||||
|
var buy_menu_handler_path: NodePath = NodePath()
|
||||||
|
|
||||||
|
var _is_open: bool = false
|
||||||
|
var _weapon_buttons: Array[Button] = []
|
||||||
|
var _populated: bool = false
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# UI Nodes (built in _build_ui)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
var _panel: Panel = null
|
||||||
|
var _money_label: Label = null
|
||||||
|
var _title_label: Label = null
|
||||||
|
var _weapon_container: Container = null
|
||||||
|
var _close_button: Button = null
|
||||||
|
var _status_label: Label = null
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Lifecycle
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_build_ui()
|
||||||
|
visible = false
|
||||||
|
|
||||||
|
# Listen for server purchase confirmations / denials
|
||||||
|
# The BuyMenuHandler sends RPCs that land on this node.
|
||||||
|
# We connect to them via the multiplayer API — they're named to match
|
||||||
|
# the RPC names defined in BuyMenuHandler.
|
||||||
|
|
||||||
|
# Connect input for toggle key
|
||||||
|
set_process_input(true)
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if event.is_action_pressed("buy_menu") or (
|
||||||
|
event is InputEventKey and
|
||||||
|
event.keycode == open_key and
|
||||||
|
event.pressed and
|
||||||
|
not event.echo
|
||||||
|
):
|
||||||
|
if _is_open:
|
||||||
|
close()
|
||||||
|
else:
|
||||||
|
open()
|
||||||
|
accept_event()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# UI Construction
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _build_ui() -> void:
|
||||||
|
# Main panel — centred, sized to fit content
|
||||||
|
_panel = Panel.new()
|
||||||
|
_panel.anchor_left = 0.15
|
||||||
|
_panel.anchor_right = 0.85
|
||||||
|
_panel.anchor_top = 0.1
|
||||||
|
_panel.anchor_bottom = 0.9
|
||||||
|
_panel.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
|
add_child(_panel)
|
||||||
|
|
||||||
|
# Panel background style
|
||||||
|
var style := StyleBoxFlat.new()
|
||||||
|
style.bg_color = panel_color
|
||||||
|
style.corner_radius_top_left = 8
|
||||||
|
style.corner_radius_top_right = 8
|
||||||
|
style.corner_radius_bottom_left = 8
|
||||||
|
style.corner_radius_bottom_right = 8
|
||||||
|
_panel.add_theme_stylebox_override("panel", style)
|
||||||
|
|
||||||
|
# Vertical layout inside the panel
|
||||||
|
var vbox := VBoxContainer.new()
|
||||||
|
vbox.anchor_left = 0.0
|
||||||
|
vbox.anchor_right = 1.0
|
||||||
|
vbox.anchor_top = 0.0
|
||||||
|
vbox.anchor_bottom = 1.0
|
||||||
|
vbox.add_theme_constant_override("separation", 12)
|
||||||
|
_panel.add_child(vbox)
|
||||||
|
|
||||||
|
# ── Title ──
|
||||||
|
_title_label = Label.new()
|
||||||
|
_title_label.text = "BUY MENU"
|
||||||
|
_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_title_label.add_theme_font_size_override("font_size", 24)
|
||||||
|
_title_label.add_theme_color_override("font_color", accent_color)
|
||||||
|
vbox.add_child(_title_label)
|
||||||
|
|
||||||
|
# ── Money display ──
|
||||||
|
_money_label = Label.new()
|
||||||
|
_money_label.text = "$0"
|
||||||
|
_money_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_money_label.add_theme_font_size_override("font_size", 20)
|
||||||
|
_money_label.add_theme_color_override("font_color", text_color)
|
||||||
|
vbox.add_child(_money_label)
|
||||||
|
|
||||||
|
# ── Weapon grid ──
|
||||||
|
_weapon_container = GridContainer.new()
|
||||||
|
_weapon_container.columns = 2
|
||||||
|
_weapon_container.add_theme_constant_override("h_separation", 16)
|
||||||
|
_weapon_container.add_theme_constant_override("v_separation", 8)
|
||||||
|
_weapon_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
_weapon_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
vbox.add_child(_weapon_container)
|
||||||
|
|
||||||
|
# ── Status / feedback line ──
|
||||||
|
_status_label = Label.new()
|
||||||
|
_status_label.text = ""
|
||||||
|
_status_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
_status_label.add_theme_color_override("font_color", Color(1.0, 0.8, 0.2, 1.0))
|
||||||
|
_status_label.add_theme_font_size_override("font_size", 14)
|
||||||
|
vbox.add_child(_status_label)
|
||||||
|
|
||||||
|
# ── Close hint ──
|
||||||
|
var close_hint := Label.new()
|
||||||
|
close_hint.text = "Press B or ESC to close | Click or press 1-4 to buy"
|
||||||
|
close_hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
close_hint.add_theme_font_size_override("font_size", 11)
|
||||||
|
close_hint.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6, 1.0))
|
||||||
|
vbox.add_child(close_hint)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public API
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Open and populate the buy menu.
|
||||||
|
func open() -> void:
|
||||||
|
if _is_open:
|
||||||
|
return
|
||||||
|
|
||||||
|
_is_open = true
|
||||||
|
visible = true
|
||||||
|
_populated = false
|
||||||
|
_clear_weapons()
|
||||||
|
_status_label.text = ""
|
||||||
|
|
||||||
|
# Request affordable weapons from the server via an RPC call
|
||||||
|
# (The server pushes the data back via RPC response.)
|
||||||
|
_request_menu_data()
|
||||||
|
|
||||||
|
buy_menu_opened.emit()
|
||||||
|
|
||||||
|
## Close the buy menu.
|
||||||
|
func close() -> void:
|
||||||
|
if not _is_open:
|
||||||
|
return
|
||||||
|
_is_open = false
|
||||||
|
visible = false
|
||||||
|
buy_menu_closed.emit()
|
||||||
|
|
||||||
|
## Returns true if the menu is currently open.
|
||||||
|
func is_open() -> bool:
|
||||||
|
return _is_open
|
||||||
|
|
||||||
|
## Update the displayed money amount.
|
||||||
|
func set_money(amount: int) -> void:
|
||||||
|
_money_label.text = "$%d" % amount
|
||||||
|
|
||||||
|
## Populate the weapon grid with purchasable items.
|
||||||
|
## `weapons` is an Array[Dictionary] with keys:
|
||||||
|
## weapon_id, display_name, cost, damage, fire_rate, mag_size
|
||||||
|
## `current_money` is the player's balance.
|
||||||
|
func populate(weapons: Array[Dictionary], current_money: int) -> void:
|
||||||
|
_clear_weapons()
|
||||||
|
_populated = false
|
||||||
|
set_money(current_money)
|
||||||
|
|
||||||
|
var index: int = 1
|
||||||
|
for w in weapons:
|
||||||
|
var affordable: bool = (current_money >= w.get("cost", 0))
|
||||||
|
_add_weapon_button(index, w, affordable)
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
_populated = true
|
||||||
|
|
||||||
|
## Show a temporary status message in the menu.
|
||||||
|
func show_status(message: String, duration: float = 3.0) -> void:
|
||||||
|
_status_label.text = message
|
||||||
|
if duration > 0:
|
||||||
|
get_tree().create_timer(duration).timeout.connect(func():
|
||||||
|
if is_instance_valid(_status_label) and _status_label.text == message:
|
||||||
|
_status_label.text = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal: weapon button creation
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _add_weapon_button(index: int, data: Dictionary, affordable: bool) -> void:
|
||||||
|
var weapon_id: String = data.get("weapon_id", "")
|
||||||
|
var display_name: String = data.get("display_name", weapon_id)
|
||||||
|
var cost: int = data.get("cost", 0)
|
||||||
|
var damage: float = data.get("damage", 0.0)
|
||||||
|
var fire_rate: float = data.get("fire_rate", 0.0)
|
||||||
|
var mag_size: int = data.get("mag_size", 0)
|
||||||
|
|
||||||
|
# Build the weapon card as a styled Button
|
||||||
|
var btn := Button.new()
|
||||||
|
btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
btn.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||||
|
btn.custom_minimum_size = Vector2(240, 80)
|
||||||
|
|
||||||
|
# Text: name + cost on first line, stats on second
|
||||||
|
var stats_line: String = "%.0f dmg | %.1f rps | %d rds" % [damage, fire_rate, mag_size]
|
||||||
|
btn.text = "[%d] %s — $%d\n%s" % [index, display_name, cost, stats_line]
|
||||||
|
btn.flat = false
|
||||||
|
btn.tooltip_text = "%s — $%d\nDamage: %.0f\nFire Rate: %.1f Hz\nMagazine: %d rounds" % [
|
||||||
|
display_name, cost, damage, fire_rate, mag_size
|
||||||
|
]
|
||||||
|
|
||||||
|
# Style
|
||||||
|
var fg_color: Color = accent_color if affordable else unaffordable_color
|
||||||
|
btn.add_theme_color_override("font_color", fg_color)
|
||||||
|
btn.add_theme_color_override("font_hover_color", text_color)
|
||||||
|
btn.add_theme_color_override("font_pressed_color", Color.WHITE)
|
||||||
|
btn.add_theme_font_size_override("font_size", 14)
|
||||||
|
|
||||||
|
if not affordable:
|
||||||
|
btn.disabled = true
|
||||||
|
|
||||||
|
btn.pressed.connect(_on_weapon_button_pressed.bind(weapon_id, cost, btn))
|
||||||
|
_weapon_container.add_child(btn)
|
||||||
|
_weapon_buttons.append(btn)
|
||||||
|
|
||||||
|
func _clear_weapons() -> void:
|
||||||
|
for child in _weapon_buttons:
|
||||||
|
if is_instance_valid(child):
|
||||||
|
child.queue_free()
|
||||||
|
_weapon_buttons.clear()
|
||||||
|
_populated = false
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal: purchase flow
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _on_weapon_button_pressed(weapon_id: String, cost: int, _button: Button) -> void:
|
||||||
|
_request_purchase(weapon_id)
|
||||||
|
|
||||||
|
## Send a purchase request RPC to the server.
|
||||||
|
func _request_purchase(weapon_id: String) -> void:
|
||||||
|
_status_label.text = "Purchasing %s..." % weapon_id
|
||||||
|
|
||||||
|
# Find the BuyMenuHandler and call its buy_request RPC.
|
||||||
|
# The handler is typically a child of GameServer.
|
||||||
|
if multiplayer.is_server():
|
||||||
|
# Listen server / standalone — call directly
|
||||||
|
_call_buy_handler(weapon_id)
|
||||||
|
else:
|
||||||
|
# Dedicated client — send RPC
|
||||||
|
_call_buy_handler_rpc(weapon_id)
|
||||||
|
|
||||||
|
func _call_buy_handler(_weapon_id: String) -> void:
|
||||||
|
# On a listen server, the handler is local
|
||||||
|
var handler: BuyMenuHandler = _find_buy_handler()
|
||||||
|
if handler:
|
||||||
|
handler.buy_request.rpc(_weapon_id)
|
||||||
|
|
||||||
|
func _call_buy_handler_rpc(weapon_id: String) -> void:
|
||||||
|
# Find the handler path and send the RPC
|
||||||
|
var handler_path: NodePath = buy_menu_handler_path
|
||||||
|
if handler_path.is_empty():
|
||||||
|
# Try to find it dynamically
|
||||||
|
handler_path = "/root/GameServer/BuyMenuHandler"
|
||||||
|
var node: Node = get_node_or_null(handler_path) if not handler_path.is_empty() else null
|
||||||
|
if node and node.has_method(&"buy_request"):
|
||||||
|
node.buy_request.rpc(weapon_id)
|
||||||
|
else:
|
||||||
|
# Fallback: call directly on the handler node path
|
||||||
|
rpc_id(1, "_remote_buy_request", weapon_id)
|
||||||
|
|
||||||
|
## Fallback: send buy request to server's handler.
|
||||||
|
@rpc("any_peer", "call_remote", "reliable")
|
||||||
|
func _remote_buy_request(weapon_id: String) -> void:
|
||||||
|
if not multiplayer.is_server():
|
||||||
|
return
|
||||||
|
var handler: BuyMenuHandler = _find_buy_handler()
|
||||||
|
if handler:
|
||||||
|
var sender: int = multiplayer.get_remote_sender_id()
|
||||||
|
handler.call_deferred("_process_buy_request", sender, weapon_id)
|
||||||
|
|
||||||
|
func _find_buy_handler() -> BuyMenuHandler:
|
||||||
|
# Try common locations
|
||||||
|
var game_server: Node = get_node_or_null("/root/GameServer")
|
||||||
|
if game_server:
|
||||||
|
for child in game_server.get_children():
|
||||||
|
if child is BuyMenuHandler:
|
||||||
|
return child
|
||||||
|
return null
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# RPC handlers — called by BuyMenuHandler (server → client)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Called by the server when a purchase succeeds.
|
||||||
|
@rpc("authority", "call_remote", "reliable")
|
||||||
|
func _on_buy_confirmed(weapon_id: String, cost: int) -> void:
|
||||||
|
show_status("Purchased %s for $%d" % [weapon_id, cost], 2.0)
|
||||||
|
purchase_completed.emit(weapon_id)
|
||||||
|
# Close after a brief delay so the player sees the confirmation
|
||||||
|
get_tree().create_timer(0.6).timeout.connect(close)
|
||||||
|
|
||||||
|
## Called by the server when a purchase is denied.
|
||||||
|
@rpc("authority", "call_remote", "reliable")
|
||||||
|
func _on_buy_denied(reason: String) -> void:
|
||||||
|
show_status("Purchase failed: %s" % reason, 3.0)
|
||||||
|
purchase_failed.emit(reason)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Request affordable weapon data from the server
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _request_menu_data() -> void:
|
||||||
|
# For now, the client populates from the weapon definitions directly,
|
||||||
|
# filtering by what the server says is affordable.
|
||||||
|
# In a more advanced version, the server sends the curated list.
|
||||||
|
#
|
||||||
|
# Since we don't know the player's money on the client, we send an
|
||||||
|
# RPC to the server to get the affordable list. But for simplicity,
|
||||||
|
# we populate all weapons and let the server deny if unaffordable.
|
||||||
|
#
|
||||||
|
# The server pushes the affordable list back via get_affordable_weapons().
|
||||||
|
# For now, just show all weapons — the server will reject unaffordable ones.
|
||||||
|
|
||||||
|
# Try to fetch from server
|
||||||
|
if multiplayer.is_server():
|
||||||
|
_populate_from_local(player_id())
|
||||||
|
else:
|
||||||
|
_request_affordable_weapons.rpc_id(1)
|
||||||
|
|
||||||
|
## Get the player's peer ID.
|
||||||
|
func player_id() -> int:
|
||||||
|
return multiplayer.get_unique_id()
|
||||||
|
|
||||||
|
## Client → Server: request affordable weapon list.
|
||||||
|
@rpc("any_peer", "call_remote", "reliable")
|
||||||
|
func _request_affordable_weapons() -> void:
|
||||||
|
if not multiplayer.is_server():
|
||||||
|
return
|
||||||
|
var sender: int = multiplayer.get_remote_sender_id()
|
||||||
|
var handler: BuyMenuHandler = _find_buy_handler()
|
||||||
|
if handler:
|
||||||
|
var weapons: Array[Dictionary] = handler.get_affordable_weapons(sender)
|
||||||
|
var money: int = handler.get_player_money(sender)
|
||||||
|
_send_menu_data.rpc_id(sender, weapons, money)
|
||||||
|
|
||||||
|
## Server → Client: send affordable weapon list and money.
|
||||||
|
@rpc("authority", "call_remote", "reliable")
|
||||||
|
func _send_menu_data(weapons: Array[Dictionary], current_money: int) -> void:
|
||||||
|
populate(weapons, current_money)
|
||||||
|
|
||||||
|
func _populate_from_local(pid: int) -> void:
|
||||||
|
var handler: BuyMenuHandler = _find_buy_handler()
|
||||||
|
if handler:
|
||||||
|
var weapons: Array[Dictionary] = handler.get_affordable_weapons(pid)
|
||||||
|
var money: int = handler.get_player_money(pid)
|
||||||
|
populate(weapons, money)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Input helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _unhandled_key_input(event: InputEventKey) -> void:
|
||||||
|
if not _is_open or not _populated:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Numeric keys 1-4 to buy
|
||||||
|
var key: Key = event.keycode
|
||||||
|
var index: int = -1
|
||||||
|
match key:
|
||||||
|
KEY_1, KEY_KP_1: index = 0
|
||||||
|
KEY_2, KEY_KP_2: index = 1
|
||||||
|
KEY_3, KEY_KP_3: index = 2
|
||||||
|
KEY_4, KEY_KP_4: index = 3
|
||||||
|
|
||||||
|
if index >= 0 and index < _weapon_buttons.size():
|
||||||
|
var btn: Button = _weapon_buttons[index]
|
||||||
|
if btn and not btn.disabled and event.pressed:
|
||||||
|
btn.emit_signal("pressed")
|
||||||
|
accept_event()
|
||||||
|
|
||||||
|
# ESC to close
|
||||||
|
if event.keycode == KEY_ESCAPE and event.pressed:
|
||||||
|
close()
|
||||||
|
accept_event()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cdsyn134gsca8
|
||||||
@@ -35,6 +35,13 @@ signal ammo_changed(current: int, max: int)
|
|||||||
## Emitted when player is killed.
|
## Emitted when player is killed.
|
||||||
signal player_killed()
|
signal player_killed()
|
||||||
|
|
||||||
|
## Emitted when the player switches weapons (for animation/UI).
|
||||||
|
signal weapon_switched(index: int, weapon_id: String)
|
||||||
|
|
||||||
|
## Emitted when the current weapon fires (for animation/visual sync).
|
||||||
|
## Connected by animation players or VFX systems on the weapon model.
|
||||||
|
signal fire_animation_triggered()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Exports — Assault Rifle Config
|
# Exports — Assault Rifle Config
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -70,6 +77,20 @@ var is_reloading: bool = false
|
|||||||
var _last_shot_time: float = 0.0
|
var _last_shot_time: float = 0.0
|
||||||
var _current_spread: float = 0.0
|
var _current_spread: float = 0.0
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Multi-weapon inventory state
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Array of weapon IDs the player carries (the inventory).
|
||||||
|
var _weapon_inventory: Array[String] = []
|
||||||
|
|
||||||
|
## Index into _weapon_inventory for the currently equipped weapon.
|
||||||
|
var _current_weapon_index: int = -1
|
||||||
|
|
||||||
|
## Per-weapon persistent state saved when switching away, restored on switch-back.
|
||||||
|
## weapon_id -> { ammo, reserve, is_reloading, last_shot_time, current_spread }
|
||||||
|
var _weapon_states: Dictionary = {}
|
||||||
|
|
||||||
## Reference to the parent FPSCharacterController (set in _ready).
|
## Reference to the parent FPSCharacterController (set in _ready).
|
||||||
var _controller: FPSCharacterController = null
|
var _controller: FPSCharacterController = null
|
||||||
|
|
||||||
@@ -132,6 +153,9 @@ func try_fire(time: float) -> bool:
|
|||||||
# Increase spread
|
# Increase spread
|
||||||
_current_spread = min(_current_spread + spread_per_shot, max_spread)
|
_current_spread = min(_current_spread + spread_per_shot, max_spread)
|
||||||
|
|
||||||
|
# Persist state back to per-weapon dict
|
||||||
|
_save_current_weapon_state()
|
||||||
|
|
||||||
# Emit fire signal (for visual/hit marker)
|
# Emit fire signal (for visual/hit marker)
|
||||||
# Direction comes from the controller's look direction
|
# Direction comes from the controller's look direction
|
||||||
if _controller:
|
if _controller:
|
||||||
@@ -141,6 +165,7 @@ func try_fire(time: float) -> bool:
|
|||||||
weapon_fired.emit(origin, direction)
|
weapon_fired.emit(origin, direction)
|
||||||
|
|
||||||
ammo_changed.emit(current_ammo, max_ammo)
|
ammo_changed.emit(current_ammo, max_ammo)
|
||||||
|
fire_animation_triggered.emit()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
## Called every frame to recover spread and handle spread animation.
|
## Called every frame to recover spread and handle spread animation.
|
||||||
@@ -165,6 +190,7 @@ func start_reload() -> void:
|
|||||||
is_reloading = false
|
is_reloading = false
|
||||||
|
|
||||||
ammo_changed.emit(current_ammo, max_ammo)
|
ammo_changed.emit(current_ammo, max_ammo)
|
||||||
|
_save_current_weapon_state()
|
||||||
|
|
||||||
## Called when a hit is confirmed by the server.
|
## Called when a hit is confirmed by the server.
|
||||||
func on_hit_confirmed(hit_pos: Vector3, damage: float, killed: bool) -> void:
|
func on_hit_confirmed(hit_pos: Vector3, damage: float, killed: bool) -> void:
|
||||||
@@ -197,9 +223,126 @@ func get_spread_direction(base_direction: Vector3) -> Vector3:
|
|||||||
|
|
||||||
## Reset weapon state (for respawn).
|
## Reset weapon state (for respawn).
|
||||||
func reset() -> void:
|
func reset() -> void:
|
||||||
current_ammo = max_ammo
|
# Re-initialise all weapon states in inventory
|
||||||
reserve_ammo = start_ammo
|
for wid in _weapon_inventory:
|
||||||
is_reloading = false
|
_initialize_weapon_state(wid)
|
||||||
_current_spread = 0.0
|
# Apply current weapon (or use defaults if no inventory)
|
||||||
_last_shot_time = 0.0
|
if _current_weapon_index >= 0 and _current_weapon_index < _weapon_inventory.size():
|
||||||
|
_apply_current_weapon()
|
||||||
|
else:
|
||||||
|
current_ammo = max_ammo
|
||||||
|
reserve_ammo = start_ammo
|
||||||
|
is_reloading = false
|
||||||
|
_current_spread = 0.0
|
||||||
|
_last_shot_time = 0.0
|
||||||
ammo_changed.emit(current_ammo, max_ammo)
|
ammo_changed.emit(current_ammo, max_ammo)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Weapon inventory management
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Add a weapon to the player's inventory.
|
||||||
|
## If no weapon is currently equipped, this one is automatically selected.
|
||||||
|
func add_weapon(weapon_id: String) -> void:
|
||||||
|
if weapon_id in _weapon_inventory:
|
||||||
|
return
|
||||||
|
_weapon_inventory.append(weapon_id)
|
||||||
|
_initialize_weapon_state(weapon_id)
|
||||||
|
if _current_weapon_index < 0:
|
||||||
|
_current_weapon_index = _weapon_inventory.size() - 1
|
||||||
|
_apply_current_weapon()
|
||||||
|
|
||||||
|
## Switch to a weapon by its inventory index.
|
||||||
|
## Returns true if the switch was performed.
|
||||||
|
func switch_weapon(index: int) -> bool:
|
||||||
|
if index < 0 or index >= _weapon_inventory.size():
|
||||||
|
return false
|
||||||
|
if index == _current_weapon_index:
|
||||||
|
return false
|
||||||
|
_save_current_weapon_state()
|
||||||
|
_current_weapon_index = index
|
||||||
|
_apply_current_weapon()
|
||||||
|
weapon_switched.emit(index, _weapon_inventory[index])
|
||||||
|
return true
|
||||||
|
|
||||||
|
## Return the WeaponData for the currently equipped weapon, or null.
|
||||||
|
func current_weapon_data() -> WeaponData:
|
||||||
|
var wid := get_current_weapon_id()
|
||||||
|
if wid.is_empty():
|
||||||
|
return null
|
||||||
|
return WeaponDefinitions.get_weapon(wid)
|
||||||
|
|
||||||
|
## Return the current weapon's ID string, or empty string.
|
||||||
|
func get_current_weapon_id() -> String:
|
||||||
|
if _current_weapon_index >= 0 and _current_weapon_index < _weapon_inventory.size():
|
||||||
|
return _weapon_inventory[_current_weapon_index]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
## Return the number of weapons in the inventory.
|
||||||
|
func get_weapon_count() -> int:
|
||||||
|
return _weapon_inventory.size()
|
||||||
|
|
||||||
|
## Return the list of weapon IDs in the inventory.
|
||||||
|
func get_inventory() -> Array[String]:
|
||||||
|
return _weapon_inventory.duplicate()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Internal: per-weapon state management
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Initialise state dict for a weapon with default values.
|
||||||
|
func _initialize_weapon_state(weapon_id: String) -> void:
|
||||||
|
var data := WeaponDefinitions.get_weapon(weapon_id)
|
||||||
|
if data == null:
|
||||||
|
return
|
||||||
|
_weapon_states[weapon_id] = {
|
||||||
|
ammo = data.mag_size,
|
||||||
|
reserve = data.mag_size * 3,
|
||||||
|
is_reloading = false,
|
||||||
|
last_shot_time = 0.0,
|
||||||
|
current_spread = 0.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
## Save the current weapon's runtime state back into _weapon_states.
|
||||||
|
func _save_current_weapon_state() -> void:
|
||||||
|
var wid := get_current_weapon_id()
|
||||||
|
if wid.is_empty() or not (wid in _weapon_states):
|
||||||
|
return
|
||||||
|
_weapon_states[wid] = {
|
||||||
|
ammo = current_ammo,
|
||||||
|
reserve = reserve_ammo,
|
||||||
|
is_reloading = is_reloading,
|
||||||
|
last_shot_time = _last_shot_time,
|
||||||
|
current_spread = _current_spread,
|
||||||
|
}
|
||||||
|
|
||||||
|
## Load the weapon at _current_weapon_index and apply its data + state
|
||||||
|
## to the live export properties and state variables.
|
||||||
|
func _apply_current_weapon() -> void:
|
||||||
|
var wid := get_current_weapon_id()
|
||||||
|
if wid.is_empty():
|
||||||
|
return
|
||||||
|
var data := WeaponDefinitions.get_weapon(wid)
|
||||||
|
if data == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Set export defaults from weapon data
|
||||||
|
max_ammo = data.mag_size
|
||||||
|
start_ammo = data.mag_size * 4 # 1 in mag + 3 reserve = 4x
|
||||||
|
fire_rate = 1.0 / max(data.fire_rate, 0.001)
|
||||||
|
reload_time = data.reload_time
|
||||||
|
spread_per_shot = data.spread_degrees * 0.1
|
||||||
|
max_spread = data.spread_degrees * 3.0
|
||||||
|
spread_recovery = data.spread_degrees * 8.0
|
||||||
|
|
||||||
|
# Restore per-weapon state
|
||||||
|
var st: Dictionary = _weapon_states.get(wid, {})
|
||||||
|
if st.is_empty():
|
||||||
|
_initialize_weapon_state(wid)
|
||||||
|
st = _weapon_states.get(wid, {})
|
||||||
|
current_ammo = st.get("ammo", data.mag_size)
|
||||||
|
reserve_ammo = st.get("reserve", data.mag_size * 3)
|
||||||
|
is_reloading = st.get("is_reloading", false)
|
||||||
|
_last_shot_time = st.get("last_shot_time", 0.0)
|
||||||
|
_current_spread = st.get("current_spread", 0.0)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://c0b8h1hq57lsu
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://pauclwpn265a
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://byjqslju7qnt6
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://jih7knhnl55a
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dc8x14rs2jyu8
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://b8jj7n3bi6ljh
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 174 B |
Binary file not shown.
|
After Width: | Height: | Size: 282 B |
@@ -0,0 +1,127 @@
|
|||||||
|
[gd_scene load_steps=11 format=4 uid="uid://br6g3dtbytwpu"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://deaf6cmuofa0" path="res://client/template/scripts/Weapon_State_Machine/PickUp Weapons/Weapon_Pick_Up.gd" id="1_mpf05"]
|
||||||
|
[ext_resource type="Script" uid="uid://bw4j1fbww4k8s" path="res://client/template/scripts/Weapon_State_Machine/WeaponSlot.gd" id="2_vl0jl"]
|
||||||
|
[ext_resource type="Resource" uid="uid://bwrs8ensewkgc" path="res://client/template/scripts/Weapon_State_Machine/Weapon_Resources/blasterL.tres" id="3_bkkd8"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_3vvaw"]
|
||||||
|
resource_local_to_scene = true
|
||||||
|
script = ExtResource("2_vl0jl")
|
||||||
|
weapon = ExtResource("3_bkkd8")
|
||||||
|
current_ammo = 30
|
||||||
|
reserve_ammo = 0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_761wa"]
|
||||||
|
resource_name = "metal"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.454902, 0.431373, 0.403922, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_757o3"]
|
||||||
|
resource_name = "darkMetal"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.454902, 0.431373, 0.403922, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_0y2bu"]
|
||||||
|
resource_name = "yellow"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.533333, 0.984314, 0.976471, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
emission_enabled = true
|
||||||
|
emission = Color(0.282353, 0.568627, 0.541176, 1)
|
||||||
|
emission_energy_multiplier = 9.4
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_bjg0s"]
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 168,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYAAwABAAgACAAJAAMADAAKAAsACwANAAwADAAOAA8ADwAKAAwAEgAQABEAEQATABIAEgAJAAgACAAQABIAEQAAAAIAAgATABEACwAEAAYABgANAAsAFQAUABEAEQAWABUAEQAQABYAEAAIABYAAAARABQAFAAXAAAAFwABAAAAFwAYAAEAGAAIAAEAGAAWAAgAGgAZAAYABgAbABoABgAHABsABwAcABsABwAOABwADgAMABwADQAGABkAGQAdAA0AHQAMAA0AHQAcAAwAGQAaABQAFAAVABkAFgAYABwAHAAdABYAFQAWAB4AHgAZABUAHgAfABkAHwAgABkAIQAeABYAFgAdACEAHQAgACEAHQAZACAAGwAXABQAFAAaABsAHAAYABcAFwAbABwABwAFAA8ADwAOAAcA"),
|
||||||
|
"name": "metal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 18,
|
||||||
|
"index_data": PackedByteArray("EgATAAIAAgADABIAAwAJABIABQAEAAsACwAKAAUACgAPAAUA"),
|
||||||
|
"name": "darkMetal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 138,
|
||||||
|
"index_data": PackedByteArray("IgAhACAAIAAjACIAIwAkACIAIwAlACQAJQAmACQAJgAnACQAJgAoACcAJgApACgAKQAqACgAKQArACoAKwAsACoALgAtACcAJwAoAC4AJQAvADAAMAAmACUAMQAfAB4AHgAyADEAMgAvADEAMgAzAC8AMwAwAC8AMwAtADAALQA0ADAALQAuADQALgA1ADQALgA2ADUANgA3ADUAJwAtADMAMwAkACcAIgAyAB4AHgAhACIANQA3ACwALAArADUANAA1ACsAKwApADQAIwAxAC8ALwAlACMAIAAfADEAMQAjACAAMAA0ACkAKQAmADAAJAAzADIAMgAiACQANgAuACgAKAAqADYANwA2ACoAKgAsADcA"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_wf2rx"]
|
||||||
|
resource_name = "clipA_Mesh clipA"
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 168,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYACgAIAAkACQALAAoADgAMAA0ADQAPAA4AEgAQABEAEQATABIAFgAUABUAFQAXABYAGgAYABkAGQAbABoAHgAcAB0AHQAfAB4AIgAgACEAIQAjACIAJgAkACUAJQAnACYAJQAoACcAKAApACcAKgAlACQAJAArACoAKwAsACoAKwAtACwALQApACwALQAnACkAMAAuAC8ALwAxADAALwAyADEAMgAzADEAMgA0ADMANAA1ADMANgAvAC4ALgA3ADYANwA1ADYANwAzADUAOgA4ADkAOQA7ADoAPgA8AD0APQA/AD4AQgBAAEEAQQBDAEIAQQBEAEMARABFAEMARgBBAEAAQABHAEYARwBFAEYARwBDAEUASgBIAEkASQBLAEoATgBMAE0ATQBPAE4AUgBQAFEAUQBTAFIA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_761wa"),
|
||||||
|
"name": "metal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAA/7+pqv//AAD/v6mq//8AAP+/qar//wAA/7+pqv//AAD/v6mq//8AAP+/qar//wAA/7+pqv//AAD/v/9///////+//3///////7//f///////v/9///////+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//f/9/////v/9//3////+//3//f////7//f/9/////v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//3//f////7//f/9/////v/9//3////+//3//f////7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//7////9//////////3/+////////f/7///////9//v///////3/+////////f/7/AID///9//v8AgP///3///wCA////f/////////9///////9//3////+//3//f////7//f/9/////v/9//3////+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//f///////v/9///////+//3///////7//f////v//v/9////+//+//3///////7//f////v//v/9///////+//3+qKv///7//f6oq////v/9/qir///+//3+qKv///7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9///////+//3///////7//f///////v/9///////+/AAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP8//78AAACA/z//vwAAAIAlUSXRAAAAgCVRJdEAAACAJdHZrgAAAID/v/+/AAAAgCXR2a4AAACA/7//vwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP8//78AAACA2S7ZrgAAAID/P/+/AAAAgNku2a4AAACADBsMmwAAAIDkDuOOAAAAgAwbDJsAAACA5A7jjgAAAID/f///AAAAgP9///8AAACADJvy5AAAAIAMm/LkAAAAgAyb8uQAAACADJvy5AAAAIDZriXRAAAAgNmuJdEAAACA8uQMmwAAAIAl0dmuAAAAgPLkDJsAAACAJdHZrgAAAIAa8eOOAAAAgPLkDJsAAACAGvHjjgAAAIDy5AybAAAAgNmuJdEAAACA2a4l0QAAAID/v/+/AAAAgP+//78AAACA2S7ZrgAAAIAMGwybAAAAgNku2a4AAACADBsMmwAAAIAlUSXRAAAAgCVRJdEAAACA8mTy5AAAAIDyZPLkAAAAgPJk8uQAAACA8mTy5AAAAID/f///AAAAgP9///8AAACA")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 18,
|
||||||
|
"index_data": PackedByteArray("VgBUAFUAVQBXAFYAVwBYAFYAWwBZAFoAWgBcAFsAXABdAFsA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_757o3"),
|
||||||
|
"name": "darkMetal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3+qKgAAAID/f6oqAAAAgP9/qioAAACA/3+qKgAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACAAAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//v////3/+////////f/////////9//v///////3//////////f///////////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP8//78AAACA/z//vwAAAIAlUSXRAAAAgCVRJdEAAACAJdHZrgAAAID/v/+/AAAAgCXR2a4AAACA/7//vwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP8//78AAACA2S7ZrgAAAID/P/+/AAAAgNku2a4AAACADBsMmwAAAIDkDuOOAAAAgAwbDJsAAACA5A7jjgAAAID/f///AAAAgP9///8AAACADJvy5AAAAIAMm/LkAAAAgAyb8uQAAACADJvy5AAAAIDZriXRAAAAgNmuJdEAAACA8uQMmwAAAIAl0dmuAAAAgPLkDJsAAACAJdHZrgAAAIAa8eOOAAAAgPLkDJsAAACAGvHjjgAAAIDy5AybAAAAgNmuJdEAAACA2a4l0QAAAID/v/+/AAAAgP+//78AAACA2S7ZrgAAAIAMGwybAAAAgNku2a4AAACADBsMmwAAAIAlUSXRAAAAgCVRJdEAAACA8mTy5AAAAIDyZPLkAAAAgPJk8uQAAACA8mTy5AAAAID/f///AAAAgP9///8AAACA")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 138,
|
||||||
|
"index_data": PackedByteArray("YABeAF8AXwBhAGAAYQBiAGAAYQBjAGIAYwBkAGIAZABlAGIAZABmAGUAZABnAGYAZwBoAGYAZwBpAGgAaQBqAGgAbQBrAGwAbABuAG0AcQBvAHAAcAByAHEAdQBzAHQAdAB2AHUAdgB3AHUAdgB4AHcAeAB5AHcAeAB6AHkAegB7AHkAegB8AHsAfAB9AHsAfAB+AH0AfgB/AH0AggCAAIEAgQCDAIIAhgCEAIUAhQCHAIYAigCIAIkAiQCLAIoAjgCMAI0AjQCPAI4AkgCQAJEAkQCTAJIAlgCUAJUAlQCXAJYAmgCYAJkAmQCbAJoAngCcAJ0AnQCfAJ4AogCgAKEAoQCjAKIApgCkAKUApQCnAKYA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_0y2bu"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3+qKgAAAID/f6oqAAAAgP9/qioAAACA/3+qKgAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID/////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/8//7//f/+//z//v/9//78lUSXR/3//vyVRJdH/f/+/JdHZrv//////v/+//////yXR2a7//////7//v///////f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/8//7//f/+/2S7Zrv9//7//P/+//3//v9ku2a7/f/+/DBsMm/9//7/kDuOO/3//vwwbDJv/f/+/5A7jjv9//7//f/////////9/////////DJvy5P////8Mm/Lk/////wyb8uT/////DJvy5P/////ZriXR/////9muJdH/////8uQMm/////8l0dmu//////LkDJv/////JdHZrv////8a8eOO//////LkDJv/////GvHjjv/////y5Ayb/////9muJdH/////2a4l0f//////v/+///////+//7//////2S7Zrv9//78MGwyb/3//v9ku2a7/f/+/DBsMm/9//78lUSXR/3//vyVRJdH/f/+/8mTy5P9//7/yZPLk/3//v/Jk8uT/f/+/8mTy5P9//7//f////3//v/9/////f/+/")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
shadow_mesh = SubResource("ArrayMesh_bjg0s")
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_y1cnp"]
|
||||||
|
size = Vector3(0.0599088, 0.250772, 0.143523)
|
||||||
|
|
||||||
|
[node name="blasterL_Clip" type="RigidBody3D"]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 5
|
||||||
|
script = ExtResource("1_mpf05")
|
||||||
|
weapon = SubResource("Resource_3vvaw")
|
||||||
|
TYPE = "Ammo"
|
||||||
|
|
||||||
|
[node name="clipA2" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1.21, 0, 0, 0, 1, 0, 0, 0)
|
||||||
|
mesh = SubResource("ArrayMesh_wf2rx")
|
||||||
|
skeleton = NodePath("")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
shape = SubResource("BoxShape3D_y1cnp")
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
[gd_scene load_steps=11 format=4 uid="uid://dl236agqx8rjp"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://deaf6cmuofa0" path="res://client/template/scripts/Weapon_State_Machine/PickUp Weapons/Weapon_Pick_Up.gd" id="1_6xwda"]
|
||||||
|
[ext_resource type="Script" uid="uid://bw4j1fbww4k8s" path="res://client/template/scripts/Weapon_State_Machine/WeaponSlot.gd" id="2_qrntt"]
|
||||||
|
[ext_resource type="Resource" uid="uid://yc1f2j7pl4vr" path="res://client/template/scripts/Weapon_State_Machine/Weapon_Resources/blasterI.tres" id="3_ot8uw"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_yxf5f"]
|
||||||
|
resource_local_to_scene = true
|
||||||
|
script = ExtResource("2_qrntt")
|
||||||
|
weapon = ExtResource("3_ot8uw")
|
||||||
|
current_ammo = 30
|
||||||
|
reserve_ammo = 0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_n3ik4"]
|
||||||
|
resource_name = "metal"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.904365, 0.914141, 0.944534, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_fil5e"]
|
||||||
|
resource_name = "darkMetal"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.793075, 0.81829, 0.89044, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_bfdxb"]
|
||||||
|
resource_name = "yellow"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.998274, 0.894448, 0.505969, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_j3k8o"]
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 168,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYAAwABAAgACAAJAAMADAAKAAsACwANAAwADAAOAA8ADwAKAAwAEgAQABEAEQATABIAEgAJAAgACAAQABIAEQAAAAIAAgATABEACwAEAAYABgANAAsAFQAUABEAEQAWABUAEQAQABYAEAAIABYAAAARABQAFAAXAAAAFwABAAAAFwAYAAEAGAAIAAEAGAAWAAgAGgAZAAYABgAbABoABgAHABsABwAcABsABwAOABwADgAMABwADQAGABkAGQAdAA0AHQAMAA0AHQAcAAwAGQAaABQAFAAVABkAFgAYABwAHAAdABYAFQAWAB4AHgAZABUAHgAfABkAHwAgABkAIQAeABYAFgAdACEAHQAgACEAHQAZACAAGwAXABQAFAAaABsAHAAYABcAFwAbABwABwAFAA8ADwAOAAcA"),
|
||||||
|
"name": "metal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 18,
|
||||||
|
"index_data": PackedByteArray("EgATAAIAAgADABIAAwAJABIABQAEAAsACwAKAAUACgAPAAUA"),
|
||||||
|
"name": "darkMetal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 138,
|
||||||
|
"index_data": PackedByteArray("IgAhACAAIAAjACIAIwAkACIAIwAlACQAJQAmACQAJgAnACQAJgAoACcAJgApACgAKQAqACgAKQArACoAKwAsACoALgAtACcAJwAoAC4AJQAvADAAMAAmACUAMQAfAB4AHgAyADEAMgAvADEAMgAzAC8AMwAwAC8AMwAtADAALQA0ADAALQAuADQALgA1ADQALgA2ADUANgA3ADUAJwAtADMAMwAkACcAIgAyAB4AHgAhACIANQA3ACwALAArADUANAA1ACsAKwApADQAIwAxAC8ALwAlACMAIAAfADEAMQAjACAAMAA0ACkAKQAmADAAJAAzADIAMgAiACQANgAuACgAKAAqADYANwA2ACoAKgAsADcA"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_py3xv"]
|
||||||
|
resource_name = "clipA_Mesh clipA"
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 168,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYACgAIAAkACQALAAoADgAMAA0ADQAPAA4AEgAQABEAEQATABIAFgAUABUAFQAXABYAGgAYABkAGQAbABoAHgAcAB0AHQAfAB4AIgAgACEAIQAjACIAJgAkACUAJQAnACYAJQAoACcAKAApACcAKgAlACQAJAArACoAKwAsACoAKwAtACwALQApACwALQAnACkAMAAuAC8ALwAxADAALwAyADEAMgAzADEAMgA0ADMANAA1ADMANgAvAC4ALgA3ADYANwA1ADYANwAzADUAOgA4ADkAOQA7ADoAPgA8AD0APQA/AD4AQgBAAEEAQQBDAEIAQQBEAEMARABFAEMARgBBAEAAQABHAEYARwBFAEYARwBDAEUASgBIAEkASQBLAEoATgBMAE0ATQBPAE4AUgBQAFEAUQBTAFIA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_n3ik4"),
|
||||||
|
"name": "metal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAA/7+pqv//AAD/v6mq//8AAP+/qar//wAA/7+pqv//AAD/v6mq//8AAP+/qar//wAA/7+pqv//AAD/v/9///////+//3///////7//f///////v/9///////+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//f/9/////v/9//3////+//3//f////7//f/9/////v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//3//f////7//f/9/////v/9//3////+//3//f////7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//7////9//////////3/+////////f/7///////9//v///////3/+////////f/7/AID///9//v8AgP///3///wCA////f/////////9///////9//3////+//3//f////7//f/9/////v/9//3////+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//f///////v/9///////+//3///////7//f////v//v/9////+//+//3///////7//f////v//v/9///////+//3+qKv///7//f6oq////v/9/qir///+//3+qKv///7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9///////+//3///////7//f///////v/9///////+/AAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP8//78AAACA/z//vwAAAIAlUSXRAAAAgCVRJdEAAACAJdHZrgAAAID/v/+/AAAAgCXR2a4AAACA/7//vwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP8//78AAACA2S7ZrgAAAID/P/+/AAAAgNku2a4AAACADBsMmwAAAIDkDuOOAAAAgAwbDJsAAACA5A7jjgAAAID/f///AAAAgP9///8AAACADJvy5AAAAIAMm/LkAAAAgAyb8uQAAACADJvy5AAAAIDZriXRAAAAgNmuJdEAAACA8uQMmwAAAIAl0dmuAAAAgPLkDJsAAACAJdHZrgAAAIAa8eOOAAAAgPLkDJsAAACAGvHjjgAAAIDy5AybAAAAgNmuJdEAAACA2a4l0QAAAID/v/+/AAAAgP+//78AAACA2S7ZrgAAAIAMGwybAAAAgNku2a4AAACADBsMmwAAAIAlUSXRAAAAgCVRJdEAAACA8mTy5AAAAIDyZPLkAAAAgPJk8uQAAACA8mTy5AAAAID/f///AAAAgP9///8AAACA")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 18,
|
||||||
|
"index_data": PackedByteArray("VgBUAFUAVQBXAFYAVwBYAFYAWwBZAFoAWgBcAFsAXABdAFsA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_fil5e"),
|
||||||
|
"name": "darkMetal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3+qKgAAAID/f6oqAAAAgP9/qioAAACA/3+qKgAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACAAAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//v////3/+////////f/////////9//v///////3//////////f///////////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP8//78AAACA/z//vwAAAIAlUSXRAAAAgCVRJdEAAACAJdHZrgAAAID/v/+/AAAAgCXR2a4AAACA/7//vwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP8//78AAACA2S7ZrgAAAID/P/+/AAAAgNku2a4AAACADBsMmwAAAIDkDuOOAAAAgAwbDJsAAACA5A7jjgAAAID/f///AAAAgP9///8AAACADJvy5AAAAIAMm/LkAAAAgAyb8uQAAACADJvy5AAAAIDZriXRAAAAgNmuJdEAAACA8uQMmwAAAIAl0dmuAAAAgPLkDJsAAACAJdHZrgAAAIAa8eOOAAAAgPLkDJsAAACAGvHjjgAAAIDy5AybAAAAgNmuJdEAAACA2a4l0QAAAID/v/+/AAAAgP+//78AAACA2S7ZrgAAAIAMGwybAAAAgNku2a4AAACADBsMmwAAAIAlUSXRAAAAgCVRJdEAAACA8mTy5AAAAIDyZPLkAAAAgPJk8uQAAACA8mTy5AAAAID/f///AAAAgP9///8AAACA")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 138,
|
||||||
|
"index_data": PackedByteArray("YABeAF8AXwBhAGAAYQBiAGAAYQBjAGIAYwBkAGIAZABlAGIAZABmAGUAZABnAGYAZwBoAGYAZwBpAGgAaQBqAGgAbQBrAGwAbABuAG0AcQBvAHAAcAByAHEAdQBzAHQAdAB2AHUAdgB3AHUAdgB4AHcAeAB5AHcAeAB6AHkAegB7AHkAegB8AHsAfAB9AHsAfAB+AH0AfgB/AH0AggCAAIEAgQCDAIIAhgCEAIUAhQCHAIYAigCIAIkAiQCLAIoAjgCMAI0AjQCPAI4AkgCQAJEAkQCTAJIAlgCUAJUAlQCXAJYAmgCYAJkAmQCbAJoAngCcAJ0AnQCfAJ4AogCgAKEAoQCjAKIApgCkAKUApQCnAKYA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_bfdxb"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3+qKgAAAID/f6oqAAAAgP9/qioAAACA/3+qKgAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID/////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/8//7//f/+//z//v/9//78lUSXR/3//vyVRJdH/f/+/JdHZrv//////v/+//////yXR2a7//////7//v///////f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/8//7//f/+/2S7Zrv9//7//P/+//3//v9ku2a7/f/+/DBsMm/9//7/kDuOO/3//vwwbDJv/f/+/5A7jjv9//7//f/////////9/////////DJvy5P////8Mm/Lk/////wyb8uT/////DJvy5P/////ZriXR/////9muJdH/////8uQMm/////8l0dmu//////LkDJv/////JdHZrv////8a8eOO//////LkDJv/////GvHjjv/////y5Ayb/////9muJdH/////2a4l0f//////v/+///////+//7//////2S7Zrv9//78MGwyb/3//v9ku2a7/f/+/DBsMm/9//78lUSXR/3//vyVRJdH/f/+/8mTy5P9//7/yZPLk/3//v/Jk8uT/f/+/8mTy5P9//7//f////3//v/9/////f/+/")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
shadow_mesh = SubResource("ArrayMesh_j3k8o")
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_y1cnp"]
|
||||||
|
size = Vector3(0.0599088, 0.407185, 0.143523)
|
||||||
|
|
||||||
|
[node name="blasterI_Clip" type="RigidBody3D"]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 5
|
||||||
|
script = ExtResource("1_6xwda")
|
||||||
|
weapon = SubResource("Resource_yxf5f")
|
||||||
|
TYPE = "Ammo"
|
||||||
|
|
||||||
|
[node name="clipA2" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1.936, 0, 0, 0, 0.99, 0, 0, 0)
|
||||||
|
mesh = SubResource("ArrayMesh_py3xv")
|
||||||
|
skeleton = NodePath("")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
shape = SubResource("BoxShape3D_y1cnp")
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
[gd_scene load_steps=11 format=4 uid="uid://cgt2lt4ultlaq"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://deaf6cmuofa0" path="res://client/template/scripts/Weapon_State_Machine/PickUp Weapons/Weapon_Pick_Up.gd" id="1_qqhy0"]
|
||||||
|
[ext_resource type="Script" uid="uid://bw4j1fbww4k8s" path="res://client/template/scripts/Weapon_State_Machine/WeaponSlot.gd" id="2_ybq5e"]
|
||||||
|
[ext_resource type="Resource" uid="uid://cd8msvjysh2uv" path="res://client/template/scripts/Weapon_State_Machine/Weapon_Resources/blasterM.tres" id="3_parqs"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_jnsso"]
|
||||||
|
resource_local_to_scene = true
|
||||||
|
script = ExtResource("2_ybq5e")
|
||||||
|
weapon = ExtResource("3_parqs")
|
||||||
|
current_ammo = 9
|
||||||
|
reserve_ammo = 0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_0eh6a"]
|
||||||
|
resource_name = "metal"
|
||||||
|
albedo_color = Color(1, 0.894118, 0.505882, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_8qvhc"]
|
||||||
|
resource_name = "darkMetal"
|
||||||
|
albedo_color = Color(0.793075, 0.81829, 0.89044, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_mihsn"]
|
||||||
|
resource_name = "yellow"
|
||||||
|
albedo_color = Color(0.998274, 0.894448, 0.505969, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_68l5q"]
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34896613377,
|
||||||
|
"index_count": 168,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYAAwABAAgACAAJAAMADAAKAAsACwANAAwADAAOAA8ADwAKAAwAEgAQABEAEQATABIAEgAJAAgACAAQABIAEQAAAAIAAgATABEACwAEAAYABgANAAsAFQAUABEAEQAWABUAEQAQABYAEAAIABYAAAARABQAFAAXAAAAFwABAAAAFwAYAAEAGAAIAAEAGAAWAAgAGgAZAAYABgAbABoABgAHABsABwAcABsABwAOABwADgAMABwADQAGABkAGQAdAA0AHQAMAA0AHQAcAAwAGQAaABQAFAAVABkAFgAYABwAHAAdABYAFQAWAB4AHgAZABUAHgAfABkAHwAgABkAIQAeABYAFgAdACEAHQAgACEAHQAZACAAGwAXABQAFAAaABsAHAAYABcAFwAbABwABwAFAA8ADwAOAAcA"),
|
||||||
|
"name": "metal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("AABxLszMAAAAAP8fk54AAFVVcS7MzAAAVVX/H5OeAACpqnEuzMwAAKmq/x+TngAA//9xLszMAAD///8fk54AAAAA/x8zMwAAVVX/HzMzAACpqlTVMzMAAKmqVNXMzAAA//9U1TMzAAD//1TVzMwAAP///x8zMwAAqar/HzMzAAAAAFTVMzMAAAAAVNXMzAAAVVVU1TMzAABVVVTVzMwAAAAAqhr//wAAAABU9f//AAAAAFT1AAAAAAAAAACpqgAAAAAAAAAAAAD//1T1//8AAP//qhr//wAA//8AAKmqAAD//wAAAAAAAP//VPUAAAAAVVVU9czMAACpqlT1zMwAAKmqVPUzMwAAVVVU9TMzAADJVhf4MzMAADWpF/gzMwAADFup+jMzAADypKn6MzMAACqe3/wzMwAA1GHf/DMzAACqapH+MzMAAFSVkf4zMwAA9HSh/zMzAAAKi6H/MzMAAP9///8zMwAA1GHf/MzMAACqapH+zMwAAPKkqfrMzAAAKp7f/MzMAAA1qRf4zMwAAMlWF/jMzAAADFup+szMAABUlZH+zMwAAAqLof/MzAAA9HSh/8zMAAD/f///zMwAAA==")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34896613377,
|
||||||
|
"index_count": 18,
|
||||||
|
"index_data": PackedByteArray("EgATAAIAAgADABIAAwAJABIABQAEAAsACwAKAAUACgAPAAUA"),
|
||||||
|
"name": "darkMetal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("AABxLszMAAAAAP8fk54AAFVVcS7MzAAAVVX/H5OeAACpqnEuzMwAAKmq/x+TngAA//9xLszMAAD///8fk54AAAAA/x8zMwAAVVX/HzMzAACpqlTVMzMAAKmqVNXMzAAA//9U1TMzAAD//1TVzMwAAP///x8zMwAAqar/HzMzAAAAAFTVMzMAAAAAVNXMzAAAVVVU1TMzAABVVVTVzMwAAAAAqhr//wAAAABU9f//AAAAAFT1AAAAAAAAAACpqgAAAAAAAAAAAAD//1T1//8AAP//qhr//wAA//8AAKmqAAD//wAAAAAAAP//VPUAAAAAVVVU9czMAACpqlT1zMwAAKmqVPUzMwAAVVVU9TMzAADJVhf4MzMAADWpF/gzMwAADFup+jMzAADypKn6MzMAACqe3/wzMwAA1GHf/DMzAACqapH+MzMAAFSVkf4zMwAA9HSh/zMzAAAKi6H/MzMAAP9///8zMwAA1GHf/MzMAACqapH+zMwAAPKkqfrMzAAAKp7f/MzMAAA1qRf4zMwAAMlWF/jMzAAADFup+szMAABUlZH+zMwAAAqLof/MzAAA9HSh/8zMAAD/f///zMwAAA==")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34896613377,
|
||||||
|
"index_count": 138,
|
||||||
|
"index_data": PackedByteArray("IgAhACAAIAAjACIAIwAkACIAIwAlACQAJQAmACQAJgAnACQAJgAoACcAJgApACgAKQAqACgAKQArACoAKwAsACoALgAtACcAJwAoAC4AJQAvADAAMAAmACUAMQAfAB4AHgAyADEAMgAvADEAMgAzAC8AMwAwAC8AMwAtADAALQA0ADAALQAuADQALgA1ADQALgA2ADUANgA3ADUAJwAtADMAMwAkACcAIgAyAB4AHgAhACIANQA3ACwALAArADUANAA1ACsAKwApADQAIwAxAC8ALwAlACMAIAAfADEAMQAjACAAMAA0ACkAKQAmADAAJAAzADIAMgAiACQANgAuACgAKAAqADYANwA2ACoAKgAsADcA"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("AABxLszMAAAAAP8fk54AAFVVcS7MzAAAVVX/H5OeAACpqnEuzMwAAKmq/x+TngAA//9xLszMAAD///8fk54AAAAA/x8zMwAAVVX/HzMzAACpqlTVMzMAAKmqVNXMzAAA//9U1TMzAAD//1TVzMwAAP///x8zMwAAqar/HzMzAAAAAFTVMzMAAAAAVNXMzAAAVVVU1TMzAABVVVTVzMwAAAAAqhr//wAAAABU9f//AAAAAFT1AAAAAAAAAACpqgAAAAAAAAAAAAD//1T1//8AAP//qhr//wAA//8AAKmqAAD//wAAAAAAAP//VPUAAAAAVVVU9czMAACpqlT1zMwAAKmqVPUzMwAAVVVU9TMzAADJVhf4MzMAADWpF/gzMwAADFup+jMzAADypKn6MzMAACqe3/wzMwAA1GHf/DMzAACqapH+MzMAAFSVkf4zMwAA9HSh/zMzAAAKi6H/MzMAAP9///8zMwAA1GHf/MzMAACqapH+zMwAAPKkqfrMzAAAKp7f/MzMAAA1qRf4zMwAAMlWF/jMzAAADFup+szMAABUlZH+zMwAAAqLof/MzAAA9HSh/8zMAAD/f///zMwAAA==")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_5ckjg"]
|
||||||
|
resource_name = "clipA_Mesh clipA"
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("MrMGkTKzrqsQkQaREJGuq+5uBpHubq6rzEwGkcxMrqvMTCGmzEy+bu5uIabubr5u7m6+bu5u+L3MTL5uzEz4vTKzluUQkZblMrPuTxCR7k8ys75uMrP4vRCRvm4Qkfi97m6W5cxMluXubu5PzEzuTzKzqtkQkarZMrPuTxCR7k/ubqrZzEyq2e5u7k/MTO5P///86cvM7k///4U1AACFNTMz7k8zM5bly8yq2aqq//+TnpblAAD//wAAhTUzM6rZAAD86VRV//9rYZbl/////8vMluXLzO5PMzPuT///hTUys/zpzEz86TKzhTXMTIU1MrP//8xM//8ys4U1zEyFNcxMVVTubvi9zExh2DKzYdgQkfi9EJG+bu5uvm4ys1VUzEzqscxMtIAys+qxMrO0gDKzVVQys12szExVVMxMXawQkSGmEJG+bjKzIaYys75uy8zuT8vMqtkzM+5Pk56W5TMzluUzM6rZMzPuT2thluXLzO5Py8yW5RCRhTXuboU1e5A+M4NvPjPHjh8xN3EfMe5zTC8QjEwvh4jmLXd35i1qhAUtlHsFLf9/uCzLzI5cMzOOXMvMQlozM0JaMzOuSjMzYkjLzK5Ky8xiSBCRhTXuboU1e5A+M4NvPjPHjh8xN3EfMRCMTC/uc0wvh4jmLXd35i1qhAUtlHsFLf9/uCzLzGJIy8yuSjMzYkgzM65Ky8wzNcvMgDczMzM1MzOANzMzkYjLzJGIMzPeisvM3oozMyZwy8wmcDMzc3LLzHNyMzMLPjMzvjvLzAs+y8y+OzMzgDczMzM1y8yAN8vMMzUzM0Jay8xCWjMzjlzLzI5cy8y+O8vMCz4zM747MzMLPsvMc3IzM3Nyy8wmcDMzJnDLzN6KMzPeisvMkYgzM5GI"),
|
||||||
|
"format": 34896613399,
|
||||||
|
"index_count": 168,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYACgAIAAkACQALAAoADgAMAA0ADQAPAA4AEgAQABEAEQATABIAFgAUABUAFQAXABYAGgAYABkAGQAbABoAHgAcAB0AHQAfAB4AIgAgACEAIQAjACIAJgAkACUAJQAnACYAJQAoACcAKAApACcAKgAlACQAJAArACoAKwAsACoAKwAtACwALQApACwALQAnACkAMAAuAC8ALwAxADAALwAyADEAMgAzADEAMgA0ADMANAA1ADMANgAvAC4ALgA3ADYANwA1ADYANwAzADUAOgA4ADkAOQA7ADoAPgA8AD0APQA/AD4AQgBAAEEAQQBDAEIAQQBEAEMARABFAEMARgBBAEAAQABHAEYARwBFAEYARwBDAEUASgBIAEkASQBLAEoATgBMAE0ATQBPAE4AUgBQAFEAUQBTAFIA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_0eh6a"),
|
||||||
|
"name": "metal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(5.90551, 11.4488, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("AABxLszM//8AAP8fk57//1VVcS7MzP//VVX/H5Oe//+pqnEuzMz//6mq/x+Tnv////9xLszM//////8fk57//wAA/x+Tnv+/AAD/HzMz/79VVf8fk57/v1VV/x8zM/+/qapU1TMz//+pqlTVzMz/////VNUzM/////9U1czM//////8fMzP/f6mq/x8zM/9///9U1TMz/3+pqlTVMzP/fwAAVNUzM///AABU1czM//9VVVTVMzP//1VVVNXMzP//VVX/HzMz/38AAP8fMzP/f1VVVNUzM/9/AABU1TMz/38AAHEuzMz//1VVcS7MzP//AABU1czM//9VVVTVzMz//6mqcS7MzP////9xLszM//+pqlTVzMz/////VNXMzP//AACqGv///78AAFTVzMz/vwAAVPX///+/AABU9QAA/78AAFTVMzP/vwAA/x8zM/+/AABxLszM/78AAAAAqar/vwAA/x+Tnv+/AAAAAAAA/7///1T1////v///cS7MzP+///+qGv///7///wAAqar/v////x+Tnv+///8AAAAA/7////8fMzP/v///VNUzM/+///9U1czM/7///1T1AAD/v///qhr///9/AACqGv///3///1T1////fwAAVPX///9/AAAAAAAA/////wAAAAD//wAAVPUAAP////9U9QAA//8AAFT1AAD/v1VVVPXMzP+/AABU9f///7///1T1////v6mqVPXMzP+/qapU9TMz/79VVVT1MzP/v///VPUAAP+/AAAAAKmqG60AAKoa//8brf//AACpqhut//+qGv//G60AAAAAAAD//wAAAACpqv////8AAAAA/////wAAqar//6mq/x+Tnv+/qar/HzMz/7////8fk57/v////x8zM/+/VVVU1czMVNVVVXEuzMxU1VVVVNUzM1TVVVX/H5OeVNVVVf8fMzNU1amqcS7MzFTVqapU1czMVNWpqv8fk55U1amqVNUzM1TVqar/HzMzVNVVVVT1MzP//6mqVPUzM///yVYX+DMz//81qRf4MzP//wxbqfozM///8qSp+jMz//8qnt/8MzP//9Rh3/wzM///qmqR/jMz//9UlZH+MzP///R0of8zM///Couh/zMz////f///MzP//9Rh3/zMzFTV1GHf/DMzVNWqapH+zMxU1apqkf4zM1TV8qSp+szMVNUqnt/8zMxU1fKkqfozM1TVKp7f/DMzVNWpqlT1zMz//1VVVPXMzP//NakX+MzM///JVhf4zMz///KkqfrMzP//DFup+szM//8qnt/8zMz//9Rh3/zMzP//VJWR/szM//+qapH+zMz//wqLof/MzP//9HSh/8zM////f///zMz//9Rh3/zMzFTVDFup+szMVNXUYd/8MzNU1QxbqfozM1TVyVYX+MzMVNVVVVT1zMxU1clWF/gzM1TVVVVU9TMzVNX/f///zMxU1f9///8zM1TVCouh/8zMVNUKi6H/MzNU1QqLof/MzFTVCouh/zMzVNVUlZH+zMxU1VSVkf4zM1TVNakX+MzMVNXypKn6zMxU1TWpF/gzM1TV8qSp+jMzVNWpqlT1zMxU1TWpF/jMzFTVqapU9TMzVNU1qRf4MzNU1VSVkf7MzFTVVJWR/jMzVNUqnt/8zMxU1Sqe3/wzM1TVDFup+szMVNXJVhf4zMxU1QxbqfozM1TVyVYX+DMzVNWqapH+zMxU1apqkf4zM1TV9HSh/8zMVNX0dKH/MzNU1fR0of/MzFTV9HSh/zMzVNX/f///zMxU1f9///8zM1TV/38az/9/Gs//fxrP/38az/9/Gs//fxrP/38az/9/Gs////9/////f////3////9//3//P/9//z//f/8//3//P/9/////f////3////9/////f/8//3//P/9//z//f/8//3////9/////f////3////9/////f////3////9/////f////3////9/////f////3/+//9/////f////3////9/////f/7//3/+//9//v//f/7//3/+//9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/f////3////9/////f////3////9/////f////3///////3/+//9/////f////3/+//9/////f/7//3////9/AAD/fwAA/38AAP9/AAD/f/9//z//f/8//3//P/9//z////9/////f////3////9//3////9/////f////3////9/////fwAA/38AAP9/AAD/fwAA/38AAP9/////f////3////9/////f////3////9/////f////3////9/////f////3////9/////v/+//7//vyXR2a4l0dmu2a7ZLv+//z/Zrtku/7//P/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//v/+/2a4l0f+//7/ZriXRDJvy5OOOGvEMm/Lk444a8f///3////9/8uTyZPLk8mTy5PJk8uTyZCXRJVEl0SVRDJsMG9mu2S4Mmwwb2a7ZLuOO5A4Mmwwb447kDgybDBsl0SVRJdElUf+//z//v/8/2a4l0Qyb8uTZriXRDJvy5CXR2a4l0dmu8uQMm/LkDJvy5Ayb8uQMm////3////9/")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("MrMGkTKzrqsQkQaREJGuq+5uBpHubq6rzEwGkcxMrqvMTCGmzEy+bu5uIabubr5u7m6+bu5u+L3MTL5uzEz4vTKzluUQkZblMrPuTxCR7k8ys75uMrP4vRCRvm4Qkfi97m6W5cxMluXubu5PzEzuTzKzqtkQkarZMrPuTxCR7k/ubqrZzEyq2e5u7k/MTO5P///86cvM7k///4U1AACFNTMz7k8zM5bly8yq2aqq//+TnpblAAD//wAAhTUzM6rZAAD86VRV//9rYZbl/////8vMluXLzO5PMzPuT///hTUys/zpzEz86TKzhTXMTIU1MrP//8xM//8ys4U1zEyFNcxMVVTubvi9zExh2DKzYdgQkfi9EJG+bu5uvm4ys1VUzEzqscxMtIAys+qxMrO0gDKzVVQys12szExVVMxMXawQkSGmEJG+bjKzIaYys75uy8zuT8vMqtkzM+5Pk56W5TMzluUzM6rZMzPuT2thluXLzO5Py8yW5RCRhTXuboU1e5A+M4NvPjPHjh8xN3EfMe5zTC8QjEwvh4jmLXd35i1qhAUtlHsFLf9/uCzLzI5cMzOOXMvMQlozM0JaMzOuSjMzYkjLzK5Ky8xiSBCRhTXuboU1e5A+M4NvPjPHjh8xN3EfMRCMTC/uc0wvh4jmLXd35i1qhAUtlHsFLf9/uCzLzGJIy8yuSjMzYkgzM65Ky8wzNcvMgDczMzM1MzOANzMzkYjLzJGIMzPeisvM3oozMyZwy8wmcDMzc3LLzHNyMzMLPjMzvjvLzAs+y8y+OzMzgDczMzM1y8yAN8vMMzUzM0Jay8xCWjMzjlzLzI5cy8y+O8vMCz4zM747MzMLPsvMc3IzM3Nyy8wmcDMzJnDLzN6KMzPeisvMkYgzM5GI"),
|
||||||
|
"format": 34896613399,
|
||||||
|
"index_count": 18,
|
||||||
|
"index_data": PackedByteArray("VgBUAFUAVQBXAFYAVwBYAFYAWwBZAFoAWgBcAFsAXABdAFsA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_8qvhc"),
|
||||||
|
"name": "darkMetal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(5.90551, 11.4488, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("AABxLszM9eAAAP8fk5714FVVcS7MzPXgVVX/H5Oe9eCpqnEuzMz14Kmq/x+TnvXg//9xLszM9eD///8fk5714AAA/x+TnlTVAAD/HzMzVNVVVf8fk55U1VVV/x8zM1TVqapU1TMzVNWpqlTVzMxU1f//VNUzM1TV//9U1czMVNX///8fMzP//6mq/x8zM/////9U1TMz//+pqlTVMzP//wAAVNUzM1TVAABU1czMVNVVVVTVMzNU1VVVVNXMzFTVVVX/HzMz//8AAP8fMzP//1VVVNUzM///AABU1TMz//8AAHEuzMz//1VVcS7MzP//AABU1czM//9VVVTVzMz//6mqcS7MzP////9xLszM//+pqlTVzMz/////VNXMzP//AACqGv//VNUAAFTVzMxU1QAAVPX//1TVAABU9QAAVNUAAFTVMzNU1QAA/x8zM1TVAABxLszMVNUAAAAAqapU1QAA/x+TnlTVAAAAAAAAVNX//1T1//9U1f//cS7MzFTV//+qGv//VNX//wAAqapU1f///x+TnlTV//8AAAAAVNX///8fMzNU1f//VNUzM1TV//9U1czMVNX//1T1AABU1f//qhr/////AACqGv///////1T1/////wAAVPX/////AAAAAAAA/////wAAAAD//wAAVPUAAP////9U9QAA//8AAFT1AABU1VVVVPXMzFTVAABU9f//VNX//1T1//9U1amqVPXMzFTVqapU9TMzVNVVVVT1MzNU1f//VPUAAFTVAAAAAKmqaMsAAKoa//9oy///AACpqmjL//+qGv//aMsAAAAAAABU1QAAAACpqlTV//8AAAAAVNX//wAAqapU1amq/x+TnlTVqar/HzMzVNX///8fk55U1f///x8zM1TVVVVU1czM/79VVXEuzMz/v1VVVNUzM/+/VVX/H5Oe/79VVf8fMzP/v6mqcS7MzP+/qapU1czM/7+pqv8fk57/v6mqVNUzM/+/qar/HzMz/79VVVT1MzP//6mqVPUzM///yVYX+DMz//81qRf4MzP//wxbqfozM///8qSp+jMz//8qnt/8MzP//9Rh3/wzM///qmqR/jMz//9UlZH+MzP///R0of8zM///Couh/zMz////f///MzP//9Rh3/zMzFTV1GHf/DMzVNWqapH+zMxU1apqkf4zM1TV8qSp+szMVNUqnt/8zMxU1fKkqfozM1TVKp7f/DMzVNWpqlT1zMz//1VVVPXMzP//NakX+MzM///JVhf4zMz///KkqfrMzP//DFup+szM//8qnt/8zMz//9Rh3/zMzP//VJWR/szM//+qapH+zMz//wqLof/MzP//9HSh/8zM////f///zMz//9Rh3/zMzFTVDFup+szMVNXUYd/8MzNU1QxbqfozM1TVyVYX+MzMVNVVVVT1zMxU1clWF/gzM1TVVVVU9TMzVNX/f///zMxU1f9///8zM1TVCouh/8zMVNUKi6H/MzNU1QqLof/MzFTVCouh/zMzVNVUlZH+zMxU1VSVkf4zM1TVNakX+MzMVNXypKn6zMxU1TWpF/gzM1TV8qSp+jMzVNWpqlT1zMxU1TWpF/jMzFTVqapU9TMzVNU1qRf4MzNU1VSVkf7MzFTVVJWR/jMzVNUqnt/8zMxU1Sqe3/wzM1TVDFup+szMVNXJVhf4zMxU1QxbqfozM1TVyVYX+DMzVNWqapH+zMxU1apqkf4zM1TV9HSh/8zMVNX0dKH/MzNU1fR0of/MzFTV9HSh/zMzVNX/f///zMxU1f9///8zM1TV////f////3////9/////f////3////9/////f////3////9/////f////3////9/AAD/fwAA/38AAP9/AAD/f/9//3//f/9//3//f/9//38AAP9/AAD/fwAA/38AAP9//3//f/9//3//f/9//3//f/9/////f////3////9/////f////3////9/////f////3////9/////f////3////9/////f////3////9/////f////3////9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/f/9//3//f/9//3//f/9//3////9/////f////3///////3////9/////f////3////9/////f////3////9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/fwAA/3////9/////f////3////9//3////9//v//f////3/+//9/////fwAA/38AAP9/AAD/fwAA/38AAP9/////f////3////9/////f////3////9/////f////3////9/////f////3////9/////v/+//7//vyXR2a4l0dmu2a7ZLv+//z/Zrtku/7//P/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//f/9//3//v/+/2a4l0f+//7/ZriXRDJvy5OOOGvEMm/Lk444a8f///3////9/8uTyZPLk8mTy5PJk8uTyZCXRJVEl0SVRDJsMG9mu2S4Mmwwb2a7ZLuOO5A4Mmwwb447kDgybDBsl0SVRJdElUf+//z//v/8/2a4l0Qyb8uTZriXRDJvy5CXR2a4l0dmu8uQMm/LkDJvy5Ayb8uQMm////3////9/")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("MrMGkTKzrqsQkQaREJGuq+5uBpHubq6rzEwGkcxMrqvMTCGmzEy+bu5uIabubr5u7m6+bu5u+L3MTL5uzEz4vTKzluUQkZblMrPuTxCR7k8ys75uMrP4vRCRvm4Qkfi97m6W5cxMluXubu5PzEzuTzKzqtkQkarZMrPuTxCR7k/ubqrZzEyq2e5u7k/MTO5P///86cvM7k///4U1AACFNTMz7k8zM5bly8yq2aqq//+TnpblAAD//wAAhTUzM6rZAAD86VRV//9rYZbl/////8vMluXLzO5PMzPuT///hTUys/zpzEz86TKzhTXMTIU1MrP//8xM//8ys4U1zEyFNcxMVVTubvi9zExh2DKzYdgQkfi9EJG+bu5uvm4ys1VUzEzqscxMtIAys+qxMrO0gDKzVVQys12szExVVMxMXawQkSGmEJG+bjKzIaYys75uy8zuT8vMqtkzM+5Pk56W5TMzluUzM6rZMzPuT2thluXLzO5Py8yW5RCRhTXuboU1e5A+M4NvPjPHjh8xN3EfMe5zTC8QjEwvh4jmLXd35i1qhAUtlHsFLf9/uCzLzI5cMzOOXMvMQlozM0JaMzOuSjMzYkjLzK5Ky8xiSBCRhTXuboU1e5A+M4NvPjPHjh8xN3EfMRCMTC/uc0wvh4jmLXd35i1qhAUtlHsFLf9/uCzLzGJIy8yuSjMzYkgzM65Ky8wzNcvMgDczMzM1MzOANzMzkYjLzJGIMzPeisvM3oozMyZwy8wmcDMzc3LLzHNyMzMLPjMzvjvLzAs+y8y+OzMzgDczMzM1y8yAN8vMMzUzM0Jay8xCWjMzjlzLzI5cy8y+O8vMCz4zM747MzMLPsvMc3IzM3Nyy8wmcDMzJnDLzN6KMzPeisvMkYgzM5GI"),
|
||||||
|
"format": 34896613399,
|
||||||
|
"index_count": 138,
|
||||||
|
"index_data": PackedByteArray("YABeAF8AXwBhAGAAYQBiAGAAYQBjAGIAYwBkAGIAZABlAGIAZABmAGUAZABnAGYAZwBoAGYAZwBpAGgAaQBqAGgAbQBrAGwAbABuAG0AcQBvAHAAcAByAHEAdQBzAHQAdAB2AHUAdgB3AHUAdgB4AHcAeAB5AHcAeAB6AHkAegB7AHkAegB8AHsAfAB9AHsAfAB+AH0AfgB/AH0AggCAAIEAgQCDAIIAhgCEAIUAhQCHAIYAigCIAIkAiQCLAIoAjgCMAI0AjQCPAI4AkgCQAJEAkQCTAJIAlgCUAJUAlQCXAJYAmgCYAJkAmQCbAJoAngCcAJ0AnQCfAJ4AogCgAKEAoQCjAKIApgCkAKUApQCnAKYA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_mihsn"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(5.90551, 11.4488, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("AABxLszM9eAAAP8fk5714FVVcS7MzPXgVVX/H5Oe9eCpqnEuzMz14Kmq/x+TnvXg//9xLszM9eD///8fk5714AAA/x+TnlTVAAD/HzMzVNVVVf8fk55U1VVV/x8zM1TVqapU1TMzVNWpqlTVzMxU1f//VNUzM1TV//9U1czMVNX///8fMzP//6mq/x8zM/////9U1TMz//+pqlTVMzP//wAAVNUzM1TVAABU1czMVNVVVVTVMzNU1VVVVNXMzFTVVVX/HzMz//8AAP8fMzP//1VVVNUzM///AABU1TMz//8AAHEuzMz//1VVcS7MzP//AABU1czM//9VVVTVzMz//6mqcS7MzP////9xLszM//+pqlTVzMz/////VNXMzP//AACqGv//VNUAAFTVzMxU1QAAVPX//1TVAABU9QAAVNUAAFTVMzNU1QAA/x8zM1TVAABxLszMVNUAAAAAqapU1QAA/x+TnlTVAAAAAAAAVNX//1T1//9U1f//cS7MzFTV//+qGv//VNX//wAAqapU1f///x+TnlTV//8AAAAAVNX///8fMzNU1f//VNUzM1TV//9U1czMVNX//1T1AABU1f//qhr/////AACqGv///////1T1/////wAAVPX/////AAAAAAAA/////wAAAAD//wAAVPUAAP////9U9QAA//8AAFT1AABU1VVVVPXMzFTVAABU9f//VNX//1T1//9U1amqVPXMzFTVqapU9TMzVNVVVVT1MzNU1f//VPUAAFTVAAAAAKmqaMsAAKoa//9oy///AACpqmjL//+qGv//aMsAAAAAAABU1QAAAACpqlTV//8AAAAAVNX//wAAqapU1amq/x+TnlTVqar/HzMzVNX///8fk55U1f///x8zM1TVVVVU1czMVNVVVXEuzMxU1VVVVNUzM1TVVVX/H5OeVNVVVf8fMzNU1amqcS7MzFTVqapU1czMVNWpqv8fk55U1amqVNUzM1TVqar/HzMzVNVVVVT1MzP//6mqVPUzM///yVYX+DMz//81qRf4MzP//wxbqfozM///8qSp+jMz//8qnt/8MzP//9Rh3/wzM///qmqR/jMz//9UlZH+MzP///R0of8zM///Couh/zMz////f///MzP//9Rh3/zMzPzF1GHf/DMz/MWqapH+zMxKyqpqkf4zM0rK8qSp+szMusIqnt/8zMz8xfKkqfozM7rCKp7f/DMz/MWpqlT1zMz/f1VVVPXMzP9/NakX+MzM/3/JVhf4zMz/f/KkqfrMzP9/DFup+szM/38qnt/8zMz/f9Rh3/zMzP9/VJWR/szM/3+qapH+zMz/fwqLof/MzP9/9HSh/8zM/3//f///zMz/f9Rh3/zMzPzFDFup+szMusLUYd/8MzP8xQxbqfozM7rCyVYX+MzMsMBVVVT1zMwrwMlWF/gzM7DAVVVU9TMzK8D/f///zMxU1f9///8zM1TVCouh/8zMd88Ki6H/MzN3zwqLof/MzHfPCouh/zMzd89UlZH+zMxKylSVkf4zM0rKNakX+MzMsMDypKn6zMy6wjWpF/gzM7DA8qSp+jMzusKpqlT1zMwrwDWpF/jMzLDAqapU9TMzK8A1qRf4MzOwwFSVkf7MzErKVJWR/jMzSsoqnt/8zMz8xSqe3/wzM/zFDFup+szMusLJVhf4zMywwAxbqfozM7rCyVYX+DMzsMCqapH+zMxKyqpqkf4zM0rK9HSh/8zMd8/0dKH/MzN3z/R0of/MzHfP9HSh/zMzd8//f///zMxU1f9///8zM1TV////f////3////9/////f////3////9/////f////3////9/////f////3////9/AAD/fwAA/38AAP9/AAD/f/9//3//f/9//3//f/9//38AAP9/AAD/fwAA/38AAP9//3//f/9//3//f/9//3//f/9/////f////3////9/////f////3////9/////f////3////9/////f////3////9/////f////3////9/////f////3////9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/f/9//3//f/9//3//f/9//3////9/////f////3///////3////9/////f////3////9/////f////3////9/AAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/fwAA/3////9/////f////3////9//3////9/////f////3////9/////fwAA/38AAP9/AAD/fwAA/38AAP9/////f////3////9/////f////3////9/////f////3////9/////f////3////9////+nADG/pwAxkuiZrtLoma7qKxUFv25/xyorFQW/bn/HP9/////f////3////9/////f////3////9/////f////3////9/////f////3////9////+nADGVJZV0/6cAMZUllXTVo1R5WqHKfFWjVHlaocp8VTVqipU1aoqf82/Jn/NvyZ/zb8mf82/JpfETCKXxEwirZpWDaisVBatmlYNqKxUFtWOagetmlYN1Y5qB62aVg2XxEwil8RMIv25/xz9uf8cVJZV01aNUeVUllXTVo1R5UuiZrtLoma7v6Z/sr+mf7K/pn+yv6Z/sqqqqqqqqqqq")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
shadow_mesh = SubResource("ArrayMesh_68l5q")
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_g84k4"]
|
||||||
|
size = Vector3(0.166504, 0.159378, 0.162319)
|
||||||
|
|
||||||
|
[node name="blasterM_clip" type="RigidBody3D"]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 5
|
||||||
|
script = ExtResource("1_qqhy0")
|
||||||
|
weapon = SubResource("Resource_jnsso")
|
||||||
|
TYPE = "Ammo"
|
||||||
|
|
||||||
|
[node name="clipA2" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(2.76, 0, 0, 0, -0.695, 9.66022e-08, 0, -6.07588e-08, -1.105, 0, 0, 0)
|
||||||
|
mesh = SubResource("ArrayMesh_5ckjg")
|
||||||
|
skeleton = NodePath("")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.53727e-05, 0.00519562, 0.00165367)
|
||||||
|
shape = SubResource("BoxShape3D_g84k4")
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
[gd_scene load_steps=11 format=4 uid="uid://cjxvrad2c6vh8"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://deaf6cmuofa0" path="res://client/template/scripts/Weapon_State_Machine/PickUp Weapons/Weapon_Pick_Up.gd" id="1_d1wy0"]
|
||||||
|
[ext_resource type="Script" uid="uid://bw4j1fbww4k8s" path="res://client/template/scripts/Weapon_State_Machine/WeaponSlot.gd" id="2_tnv4c"]
|
||||||
|
[ext_resource type="Resource" uid="uid://c1jg0ifn7yvve" path="res://client/template/scripts/Weapon_State_Machine/Weapon_Resources/blasterN.tres" id="3_x72lj"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_8ete7"]
|
||||||
|
resource_local_to_scene = true
|
||||||
|
script = ExtResource("2_tnv4c")
|
||||||
|
weapon = ExtResource("3_x72lj")
|
||||||
|
current_ammo = 5
|
||||||
|
reserve_ammo = 0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_n3ik4"]
|
||||||
|
resource_name = "metal"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.904365, 0.914141, 0.944534, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_fil5e"]
|
||||||
|
resource_name = "darkMetal"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.793075, 0.81829, 0.89044, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_bfdxb"]
|
||||||
|
resource_name = "yellow"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.998274, 0.894448, 0.505969, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_j3k8o"]
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 168,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYAAwABAAgACAAJAAMADAAKAAsACwANAAwADAAOAA8ADwAKAAwAEgAQABEAEQATABIAEgAJAAgACAAQABIAEQAAAAIAAgATABEACwAEAAYABgANAAsAFQAUABEAEQAWABUAEQAQABYAEAAIABYAAAARABQAFAAXAAAAFwABAAAAFwAYAAEAGAAIAAEAGAAWAAgAGgAZAAYABgAbABoABgAHABsABwAcABsABwAOABwADgAMABwADQAGABkAGQAdAA0AHQAMAA0AHQAcAAwAGQAaABQAFAAVABkAFgAYABwAHAAdABYAFQAWAB4AHgAZABUAHgAfABkAHwAgABkAIQAeABYAFgAdACEAHQAgACEAHQAZACAAGwAXABQAFAAaABsAHAAYABcAFwAbABwABwAFAA8ADwAOAAcA"),
|
||||||
|
"name": "metal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 18,
|
||||||
|
"index_data": PackedByteArray("EgATAAIAAgADABIAAwAJABIABQAEAAsACwAKAAUACgAPAAUA"),
|
||||||
|
"name": "darkMetal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 138,
|
||||||
|
"index_data": PackedByteArray("IgAhACAAIAAjACIAIwAkACIAIwAlACQAJQAmACQAJgAnACQAJgAoACcAJgApACgAKQAqACgAKQArACoAKwAsACoALgAtACcAJwAoAC4AJQAvADAAMAAmACUAMQAfAB4AHgAyADEAMgAvADEAMgAzAC8AMwAwAC8AMwAtADAALQA0ADAALQAuADQALgA1ADQALgA2ADUANgA3ADUAJwAtADMAMwAkACcAIgAyAB4AHgAhACIANQA3ACwALAArADUANAA1ACsAKwApADQAIwAxAC8ALwAlACMAIAAfADEAMQAjACAAMAA0ACkAKQAmADAAJAAzADIAMgAiACQANgAuACgAKAAqADYANwA2ACoAKgAsADcA"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 56,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9kML1vFyPwr2amZk9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vJDC9b3NzMw8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1PJDC9b2amZm9kML1PK1H4T2amZm9CtcjvK1H4T3rUTg9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9CtejuyYE8z3rUTg9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTg9CtejOyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_py3xv"]
|
||||||
|
resource_name = "clipA_Mesh clipA"
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 168,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYACgAIAAkACQALAAoADgAMAA0ADQAPAA4AEgAQABEAEQATABIAFgAUABUAFQAXABYAGgAYABkAGQAbABoAHgAcAB0AHQAfAB4AIgAgACEAIQAjACIAJgAkACUAJQAnACYAJQAoACcAKAApACcAKgAlACQAJAArACoAKwAsACoAKwAtACwALQApACwALQAnACkAMAAuAC8ALwAxADAALwAyADEAMgAzADEAMgA0ADMANAA1ADMANgAvAC4ALgA3ADYANwA1ADYANwAzADUAOgA4ADkAOQA7ADoAPgA8AD0APQA/AD4AQgBAAEEAQQBDAEIAQQBEAEMARABFAEMARgBBAEAAQABHAEYARwBFAEYARwBDAEUASgBIAEkASQBLAEoATgBMAE0ATQBPAE4AUgBQAFEAUQBTAFIA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_n3ik4"),
|
||||||
|
"name": "metal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAA/7+pqv//AAD/v6mq//8AAP+/qar//wAA/7+pqv//AAD/v6mq//8AAP+/qar//wAA/7+pqv//AAD/v/9///////+//3///////7//f///////v/9///////+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//f/9/////v/9//3////+//3//f////7//f/9/////v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//3//f////7//f/9/////v/9//3////+//3//f////7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//7////9//////////3/+////////f/7///////9//v///////3/+////////f/7/AID///9//v8AgP///3///wCA////f/////////9///////9//3////+//3//f////7//f/9/////v/9//3////+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//f///////v/9///////+//3///////7//f////v//v/9////+//+//3///////7//f////v//v/9///////+//3+qKv///7//f6oq////v/9/qir///+//3+qKv///7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9///////+//3///////7//f///////v/9///////+/AAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP8//78AAACA/z//vwAAAIAlUSXRAAAAgCVRJdEAAACAJdHZrgAAAID/v/+/AAAAgCXR2a4AAACA/7//vwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP8//78AAACA2S7ZrgAAAID/P/+/AAAAgNku2a4AAACADBsMmwAAAIDkDuOOAAAAgAwbDJsAAACA5A7jjgAAAID/f///AAAAgP9///8AAACADJvy5AAAAIAMm/LkAAAAgAyb8uQAAACADJvy5AAAAIDZriXRAAAAgNmuJdEAAACA8uQMmwAAAIAl0dmuAAAAgPLkDJsAAACAJdHZrgAAAIAa8eOOAAAAgPLkDJsAAACAGvHjjgAAAIDy5AybAAAAgNmuJdEAAACA2a4l0QAAAID/v/+/AAAAgP+//78AAACA2S7ZrgAAAIAMGwybAAAAgNku2a4AAACADBsMmwAAAIAlUSXRAAAAgCVRJdEAAACA8mTy5AAAAIDyZPLkAAAAgPJk8uQAAACA8mTy5AAAAID/f///AAAAgP9///8AAACA")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 18,
|
||||||
|
"index_data": PackedByteArray("VgBUAFUAVQBXAFYAVwBYAFYAWwBZAFoAWgBcAFsAXABdAFsA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_fil5e"),
|
||||||
|
"name": "darkMetal",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3+qKgAAAID/f6oqAAAAgP9/qioAAACA/3+qKgAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACAAAD/f/9//78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//v////3/+////////f/////////9//v///////3//////////f///////////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP8//78AAACA/z//vwAAAIAlUSXRAAAAgCVRJdEAAACAJdHZrgAAAID/v/+/AAAAgCXR2a4AAACA/7//vwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP8//78AAACA2S7ZrgAAAID/P/+/AAAAgNku2a4AAACADBsMmwAAAIDkDuOOAAAAgAwbDJsAAACA5A7jjgAAAID/f///AAAAgP9///8AAACADJvy5AAAAIAMm/LkAAAAgAyb8uQAAACADJvy5AAAAIDZriXRAAAAgNmuJdEAAACA8uQMmwAAAIAl0dmuAAAAgPLkDJsAAACAJdHZrgAAAIAa8eOOAAAAgPLkDJsAAACAGvHjjgAAAIDy5AybAAAAgNmuJdEAAACA2a4l0QAAAID/v/+/AAAAgP+//78AAACA2S7ZrgAAAIAMGwybAAAAgNku2a4AAACADBsMmwAAAIAlUSXRAAAAgCVRJdEAAACA8mTy5AAAAIDyZPLkAAAAgPJk8uQAAACA8mTy5AAAAID/f///AAAAgP9///8AAACA")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.03, -0.12, -0.075, 0.06, 0.24, 0.15),
|
||||||
|
"attribute_data": PackedByteArray("XS6XP0T3Qj9dLpc/dxH6PyaTyT5E90I/JpPJPncR+j8mk8m+RPdCPyaTyb53Efo/XS6Xv0T3Qj9dLpe/dxH6P10ul7+US9o/XS6XvxaLRb8mk8m+lEvaPyaTyb4Wi0W/JpPJvhaLRb8mk8m+xmIxQF0ul78Wi0W/XS6Xv8ZiMUBdLpc/xmKRQCaTyT7GYpFAXS6XPyaTCcAmk8k+JpMJwF0ulz8Wi0W/XS6XP8ZiMUAmk8k+FotFvyaTyT7GYjFAJpPJvsZikUBdLpe/xmKRQCaTyb4mkwnAXS6XvyaTCcBdLpc/hlOAQCaTyT6GU4BAXS6XPyaTCcAmk8k+JpMJwCaTyb6GU4BAXS6Xv4ZTgEAmk8m+JpMJwF0ul78mkwnA9Pk8QF6vl0CLxeI/JpMJwPT5PEBUKlXA9Pk8wFQqVcCLxeK/JpMJwIvF4r/GYpFAi8XiP4ZTgEDw93s/XS63QCmXND/GYpFA9Pk8wF0ut0D0+TzAVCpVwIvF4r+GU4BA9Pk8wF6vl0Dw93u/XS63QCmXNL/GYpFA9Pk8QF0ut0CLxeI/xmKRQIvF4j8mkwnAi8XivyaTCcD0+TxAVCpVwF0ulz9er5dAXS6Xv16vl0BdLpc/VCpVwF0ul79UKlXAXS6XP10ut0BdLpe/XS63QF0ulz9UKlXAXS6Xv1QqVcBdLpe/6PP5vyaTyb7GYjFAXS6Xv/T5fEBdLpc/9Pl8QCaTyT7GYjFAJpPJPhaLRb8mk8m+FotFv10ulz/o8/m/XS6Xv+3gDkBdLpe/QIgBPV0ulz/t4A5AXS6XP0CIAT1dLpc/6PP5v10ulz/4+/0/XS6Xv+jz+b9dLpe/+Pv9PyaTyT6US9o/JpPJPhaLRb9dLpc/lEvaP10ulz8Wi0W/i8XiPyaTCcCLxeI/hlOAQIvF4r8mkwnAKZc0P8ZikUCLxeK/xmKRQIvF4r+GU4BAi8XivyaTCcAplzS/xmKRQIvF4j8mkwnAi8XiP8ZikUAmk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI6+cvtmwPCIjj5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcDxsKoOulxuwIvF4j+g4Mq/i8Xiv6Dgyr+LxeI/agjYv4vF4r9qCNi/i8Xiv+mZGMCLxeK/zS0fwIvF4j/pmRjAi8XiP80tH8Amk8k+VCpVwCaTyb5UKlXA0bTCPtCvW8DRtMK+0K9bwKORrj6Iw2HAo5GuvojDYcDwiI4+cvtmwPCIjr5y+2bAJpNJPor8asAmk0m+ivxqwIOv0D3ugG3Ag6/Qve6AbcAJafYNulxuwIvF4j/NLR/Ai8XiP+mZGMCLxeK/zS0fwIvF4r/pmRjAi8XiP0AVVsCLxeI/XIFPwIvF4r9AFVbAi8Xiv1yBT8CLxeK/QEXEPovF4j9ARcQ+i8Xiv2Dk+D6LxeI/YOT4PovF4r/4azW/i8XiP/hrNb+LxeK/aBwbv4vF4j9oHBu/i8Xiv0/HPMCLxeK/MltDwIvF4j9PxzzAi8XiPzJbQ8CLxeK/XIFPwIvF4r9AFVbAi8XiP1yBT8CLxeI/QBVWwIvF4r9qCNi/i8XiP2oI2L+LxeK/oODKv4vF4j+g4Mq/i8XiPzJbQ8CLxeI/T8c8wIvF4r8yW0PAi8Xiv0/HPMCLxeI/aBwbv4vF4r9oHBu/i8XiP/hrNb+LxeK/+Gs1v4vF4j9g5Pg+i8Xiv2Dk+D6LxeI/QEXEPovF4r9ARcQ+"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 138,
|
||||||
|
"index_data": PackedByteArray("YABeAF8AXwBhAGAAYQBiAGAAYQBjAGIAYwBkAGIAZABlAGIAZABmAGUAZABnAGYAZwBoAGYAZwBpAGgAaQBqAGgAbQBrAGwAbABuAG0AcQBvAHAAcAByAHEAdQBzAHQAdAB2AHUAdgB3AHUAdgB4AHcAeAB5AHcAeAB6AHkAegB7AHkAegB8AHsAfAB9AHsAfAB+AH0AfgB/AH0AggCAAIEAgQCDAIIAhgCEAIUAhQCHAIYAigCIAIkAiQCLAIoAjgCMAI0AjQCPAI4AkgCQAJEAkQCTAJIAlgCUAJUAlQCXAJYAmgCYAJkAmQCbAJoAngCcAJ0AnQCfAJ4AogCgAKEAoQCjAKIApgCkAKUApQCnAKYA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_bfdxb"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 168,
|
||||||
|
"vertex_data": PackedByteArray("kML1vIaWnL3rUTg9kML1vOtRuL2wyJI8CtcjvIaWnL3rUTg9CtcjvOtRuL2wyJI8CtcjPIaWnL3rUTg9CtcjPOtRuL2wyJI8kML1PIaWnL3rUTg9kML1POtRuL2wyJI8kML1vOtRuL2wyJI8kML1vOtRuL3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPArXoz3rUTi9CtcjPArXoz3rUTg9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1POtRuL3rUTi9CtcjPOtRuL3rUTi9kML1PArXoz3rUTi9CtcjPArXoz3rUTi9kML1vArXoz3rUTi9kML1vArXoz3rUTg9CtcjvArXoz3rUTi9CtcjvArXoz3rUTg9CtcjvOtRuL3rUTi9kML1vOtRuL3rUTi9CtcjvArXoz3rUTi9kML1vArXoz3rUTi9kML1vIaWnL3rUTg9CtcjvIaWnL3rUTg9kML1vArXoz3rUTg9CtcjvArXoz3rUTg9CtcjPIaWnL3rUTg9kML1PIaWnL3rUTg9CtcjPArXoz3rUTg9kML1PArXoz3rUTg9kML1vFyPwr2amZk9kML1vArXoz3rUTg9kML1vK1H4T2amZk9kML1vK1H4T2amZm9kML1vArXoz3rUTi9kML1vOtRuL3rUTi9kML1vIaWnL3rUTg9kML1vJDC9b3NzMw8kML1vOtRuL2wyJI8kML1vJDC9b2amZm9kML1PK1H4T2amZk9kML1PIaWnL3rUTg9kML1PFyPwr2amZk9kML1PJDC9b3NzMw8kML1POtRuL2wyJI8kML1PJDC9b2amZm9kML1POtRuL3rUTi9kML1PArXoz3rUTi9kML1PArXoz3rUTg9kML1PK1H4T2amZm9kML1PFyPwr2amZk9kML1vFyPwr2amZk9kML1PK1H4T2amZk9kML1vK1H4T2amZk9kML1vJDC9b2amZm9kML1PJDC9b2amZm9kML1vK1H4T2amZm9kML1PK1H4T2amZm9kML1vK1H4T2amZm9CtcjvK1H4T3rUTg9kML1vK1H4T2amZk9kML1PK1H4T2amZk9CtcjPK1H4T3rUTg9CtcjPK1H4T3rUTi9CtcjvK1H4T3rUTi9kML1PK1H4T2amZm9kML1vJDC9b3NzMw8kML1vFyPwr2amZk9kML1PJDC9b3NzMw8kML1PFyPwr2amZk9kML1vJDC9b2amZm9kML1vJDC9b3NzMw8kML1PJDC9b2amZm9kML1PJDC9b3NzMw8CtcjPOtRuL2wyJI8CtcjPOtRuL3rUTi9kML1POtRuL2wyJI8kML1POtRuL3rUTi9CtcjvArXoz3rUTg9CtcjvIaWnL3rUTg9CtcjvArXoz3rUTi9CtcjvOtRuL2wyJI8CtcjvOtRuL3rUTi9CtcjPIaWnL3rUTg9CtcjPArXoz3rUTg9CtcjPOtRuL2wyJI8CtcjPArXoz3rUTi9CtcjPOtRuL3rUTi9CtcjvK1H4T3rUTi9CtcjPK1H4T3rUTi93kEevKKU5j3rUTi93kEePKKU5j3rUTi9vOMNvB+F6z3rUTi9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTi9CtejOyYE8z3rUTi9oZ4pu+kP9T3rUTi9oZ4pO+kP9T3rUTi9AAAAAJDC9T3rUTi9a7Tnu/TC7z3rUTg9a7Tnu/TC7z3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9vOMNPB+F6z3rUTg9a7TnO/TC7z3rUTg9vOMNPB+F6z3rUTi9a7TnO/TC7z3rUTi9CtcjPK1H4T3rUTg9CtcjvK1H4T3rUTg93kEePKKU5j3rUTg93kEevKKU5j3rUTg9vOMNPB+F6z3rUTg9vOMNvB+F6z3rUTg9a7TnO/TC7z3rUTg9a7Tnu/TC7z3rUTg9CtejOyYE8z3rUTg9CtejuyYE8z3rUTg9oZ4pO+kP9T3rUTg9oZ4pu+kP9T3rUTg9AAAAAJDC9T3rUTg9a7Tnu/TC7z3rUTg9vOMNvB+F6z3rUTg9a7Tnu/TC7z3rUTi9vOMNvB+F6z3rUTi93kEevKKU5j3rUTg9CtcjvK1H4T3rUTg93kEevKKU5j3rUTi9CtcjvK1H4T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9oZ4pO+kP9T3rUTg9oZ4pO+kP9T3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi93kEePKKU5j3rUTg9vOMNPB+F6z3rUTg93kEePKKU5j3rUTi9vOMNPB+F6z3rUTi9CtcjPK1H4T3rUTg93kEePKKU5j3rUTg9CtcjPK1H4T3rUTi93kEePKKU5j3rUTi9CtejOyYE8z3rUTg9CtejOyYE8z3rUTi9a7TnO/TC7z3rUTg9a7TnO/TC7z3rUTi9vOMNvB+F6z3rUTg93kEevKKU5j3rUTg9vOMNvB+F6z3rUTi93kEevKKU5j3rUTi9CtejuyYE8z3rUTg9CtejuyYE8z3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9oZ4pu+kP9T3rUTg9oZ4pu+kP9T3rUTi9AAAAAJDC9T3rUTg9AAAAAJDC9T3rUTi9qar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgKmq//8AAACAqar//wAAAICpqv//AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9/AAAAAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/3//fwAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/////AAAAgP////8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP9//38AAACA/3//fwAAAID/f/9/AAAAgP9//38AAACA/////wAAAID/////AAAAgP////8AAACA/////wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACA/3+qKgAAAID/f6oqAAAAgP9/qioAAACA/3+qKgAAAID/fwAAAAAAgP9/AAAAAACA/38AAAAAAID/fwAAAAAAgP9///8AAACA/3///wAAAID/f///AAAAgP9///8AAACAAAD/fwAAAIAAAP9/AAAAgAAA/38AAACAAAD/fwAAAIAAAP9/AAAAgP///38AAACA////fwAAAID///9/AAAAgP///38AAACA////fwAAAID/////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/////8AAP+//////wAA/7//////AAD/v/8//7//f/+//z//v/9//78lUSXR/3//vyVRJdH/f/+/JdHZrv//////v/+//////yXR2a7//////7//v///////f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/9//3////+//3//f////7//f/9/////v/8//7//f/+/2S7Zrv9//7//P/+//3//v9ku2a7/f/+/DBsMm/9//7/kDuOO/3//vwwbDJv/f/+/5A7jjv9//7//f/////////9/////////DJvy5P////8Mm/Lk/////wyb8uT/////DJvy5P/////ZriXR/////9muJdH/////8uQMm/////8l0dmu//////LkDJv/////JdHZrv////8a8eOO//////LkDJv/////GvHjjv/////y5Ayb/////9muJdH/////2a4l0f//////v/+///////+//7//////2S7Zrv9//78MGwyb/3//v9ku2a7/f/+/DBsMm/9//78lUSXR/3//vyVRJdH/f/+/8mTy5P9//7/yZPLk/3//v/Jk8uT/f/+/8mTy5P9//7//f////3//v/9/////f/+/")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
shadow_mesh = SubResource("ArrayMesh_j3k8o")
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="ConvexPolygonShape3D_p04et"]
|
||||||
|
points = PackedVector3Array(0.03, 0.109992, 0.075, 0.00258418, 0.119648, 0.0449883, 0, 0.12, 0.0449883, -0.03, 0.109992, 0.075, 0.03, -0.0950039, 0.075, 0.03, 0.109992, -0.075, 0.00258418, 0.119648, -0.0450029, 0, 0.12, -0.0450029, -0.00259006, 0.119648, 0.0449883, -0.03, 0.109992, -0.075, -0.03, -0.0950039, 0.075, 0.03, -0.12, 0.0249902, 0.03, -0.12, -0.075, -0.00259006, 0.119648, -0.0450029, -0.03, -0.12, -0.075, -0.03, -0.12, 0.0249902)
|
||||||
|
|
||||||
|
[node name="blasterN_Clip" type="RigidBody3D"]
|
||||||
|
collision_layer = 8
|
||||||
|
collision_mask = 5
|
||||||
|
script = ExtResource("1_d1wy0")
|
||||||
|
weapon = SubResource("Resource_8ete7")
|
||||||
|
TYPE = "Ammo"
|
||||||
|
|
||||||
|
[node name="clipA2" type="MeshInstance3D" parent="."]
|
||||||
|
mesh = SubResource("ArrayMesh_py3xv")
|
||||||
|
skeleton = NodePath("")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
shape = SubResource("ConvexPolygonShape3D_p04et")
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://dc48o4niy1mok"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b26eegme16mfw" path="res://client/template/scripts/Projectiles/Projectile.gd" id="1_epqi3"]
|
||||||
|
|
||||||
|
[node name="Projectile" type="Node3D"]
|
||||||
|
script = ExtResource("1_epqi3")
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://bpxa3je174q43"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b26eegme16mfw" path="res://client/template/scripts/Projectiles/Projectile.gd" id="1_won33"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dxlkgbr1p580" path="res://client/template/Spawnable_Objects/bullet.tscn" id="2_sq5da"]
|
||||||
|
|
||||||
|
[node name="Projectile" type="Node3D"]
|
||||||
|
script = ExtResource("1_won33")
|
||||||
|
Projectile_Type = "Rigidbody_Projectile"
|
||||||
|
Projectile_Velocity = 20
|
||||||
|
Rigid_Body_Projectile = ExtResource("2_sq5da")
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 990 B |
@@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://6rfh1ejt4m6m"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://bfn8eu00l4btq" path="res://client/template/scripts/Projectiles/melee_weapon_projectile.gd" id="1_qlmbe"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_q86ac"]
|
||||||
|
|
||||||
|
[node name="MeleeProjectile" type="Node3D"]
|
||||||
|
script = ExtResource("1_qlmbe")
|
||||||
|
Projectile_Type = "over_ride"
|
||||||
|
|
||||||
|
[node name="MeleeHitbox" type="ShapeCast3D" parent="."]
|
||||||
|
shape = SubResource("BoxShape3D_q86ac")
|
||||||
|
target_position = Vector3(0, 0, -1)
|
||||||
|
collision_mask = 37
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://ddfiivxgnakti"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://biivm72fp7af8" path="res://client/template/scripts/Projectiles/rocket_launcher_projectile.gd" id="1_dkmt1"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cqhlfy6ccomm3" path="res://client/template/Spawnable_Objects/rocket.tscn" id="2_u24w0"]
|
||||||
|
|
||||||
|
[sub_resource type="SphereShape3D" id="SphereShape3D_wqjlt"]
|
||||||
|
radius = 5.0
|
||||||
|
|
||||||
|
[node name="Projectile" type="Node3D" node_paths=PackedStringArray("explosion")]
|
||||||
|
script = ExtResource("1_dkmt1")
|
||||||
|
explosion = NodePath("ExplosionRadias")
|
||||||
|
Projectile_Type = "Rigidbody_Projectile"
|
||||||
|
Projectile_Velocity = 20
|
||||||
|
Expirey_Time = 20
|
||||||
|
Rigid_Body_Projectile = ExtResource("2_u24w0")
|
||||||
|
pass_through = true
|
||||||
|
|
||||||
|
[node name="ExplosionRadias" type="ShapeCast3D" parent="."]
|
||||||
|
top_level = true
|
||||||
|
shape = SubResource("SphereShape3D_wqjlt")
|
||||||
|
target_position = Vector3(0, 0, 0)
|
||||||
|
collision_mask = 37
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://brvfyahytwd8m"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dnh4a4vkl0cl1" path="res://client/template/scripts/Projectiles/shotgun_projectile.gd" id="1_idx2q"]
|
||||||
|
|
||||||
|
[sub_resource type="Curve2D" id="Curve2D_2lhxk"]
|
||||||
|
_data = {
|
||||||
|
"points": PackedVector2Array(0, 0, 0, 0, 7, 3, 0, 0, 0, 0, -30, -11, 0, 0, 0, 0, -8, -18, 0, 0, 0, 0, 8, -9, -2.50282, -11.0124, 2.50282, 11.0124, 19, 13, 0, 0, 0, 0, -21, 1, 0, 0, 0, 0, 22, 22, 0, 0, 0, 0, 12, 21, 0, 0, 0, 0, -33, 4)
|
||||||
|
}
|
||||||
|
point_count = 9
|
||||||
|
|
||||||
|
[node name="shotgun_projectile" type="Node3D"]
|
||||||
|
script = ExtResource("1_idx2q")
|
||||||
|
Randomness = 2.941
|
||||||
|
Split_Damage = true
|
||||||
|
|
||||||
|
[node name="shotgun_pattern" type="Path2D" parent="."]
|
||||||
|
curve = SubResource("Curve2D_2lhxk")
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
extends Node
|
||||||
|
class_name Spray_Profile
|
||||||
|
|
||||||
|
@export_category("Random_Spray")
|
||||||
|
@export var Spray_Noise: FastNoiseLite
|
||||||
|
@export_range(0.0,10.0) var Random_Spray_Multiplier: float = 1.0
|
||||||
|
@export var Max_Limit: int = 30
|
||||||
|
@export var Updwards_Lock: bool = false
|
||||||
|
@export var True_Random: bool = true
|
||||||
|
|
||||||
|
@export_category("Path_Spray")
|
||||||
|
@export var Spray_Path: Path2D
|
||||||
|
@export_range(0.0,10.0) var Path_Spray_Multiplier: float = 1.0
|
||||||
|
@export_range(0.0,1.0) var Spray_Mix: float = 1.0
|
||||||
|
|
||||||
|
var Random_Spray: Vector2 = Vector2.ZERO
|
||||||
|
var Spray_Vector: Vector2 = Vector2.ZERO
|
||||||
|
|
||||||
|
func Get_Spray(count: int,_max_count: int)->Vector2:
|
||||||
|
if Spray_Noise:
|
||||||
|
if True_Random:
|
||||||
|
Spray_Noise.set_seed(randi())
|
||||||
|
|
||||||
|
var x = Spray_Noise.get_noise_1d(count)*min(count,Max_Limit)
|
||||||
|
var y = Spray_Noise.get_noise_2d(_max_count,count)*min(count,Max_Limit)
|
||||||
|
|
||||||
|
if Updwards_Lock:
|
||||||
|
y = -abs(y)
|
||||||
|
|
||||||
|
Random_Spray = Vector2(x,y)*Random_Spray_Multiplier
|
||||||
|
|
||||||
|
if Spray_Path:
|
||||||
|
var Path_Points: Curve2D = Spray_Path.get_curve()
|
||||||
|
var Point_Count: int = Path_Points.get_point_count()
|
||||||
|
count = min(count-1,Point_Count-1)
|
||||||
|
|
||||||
|
Spray_Vector = Path_Points.get_point_position(count)*Path_Spray_Multiplier
|
||||||
|
|
||||||
|
|
||||||
|
var Spray = Spray_Vector + (Random_Spray*Spray_Mix)
|
||||||
|
return Spray
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dvcpsronqmkmr
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://ciu0xkaw0ioef"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dvcpsronqmkmr" path="res://client/template/Spawnable_Objects/SprayProfiles/spray_profile.gd" id="1_60v4d"]
|
||||||
|
|
||||||
|
[sub_resource type="FastNoiseLite" id="FastNoiseLite_4msk4"]
|
||||||
|
|
||||||
|
[sub_resource type="Curve2D" id="Curve2D_0vouc"]
|
||||||
|
_data = {
|
||||||
|
"points": PackedVector2Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, -11, 0, 0, 0, 0, 1, -23, 0, 0, 0, 0, 2, -35, 0, 0, 0, 0, -4, -50, 0, 0, 0, 0, -7, -62, 0, 0, 0, 0, -12, -69, 0, 0, 0, 0, -6, -76, 0, 0, 0, 0, 12, -74, 0, 0, 0, 0, 9, -86, 0, 0, 0, 0, 16, -81, 0, 0, 0, 0, 21, -76, 0, 0, 0, 0, 19, -83, 0, 0, 0, 0, 23, -84, 0, 0, 0, 0, 36, -82, 0, 0, 0, 0, 35, -79, 0, 0, 0, 0, 28, -81, 0, 0, 0, 0, 23, -81, 0, 0, 0, 0, 5, -88, 0, 0, 0, 0, 2, -90, 0, 0, 0, 0, -6, -88, 0, 0, 0, 0, -10, -91, 0, 0, 0, 0, -12, -91, 0, 0, 0, 0, -13, -89, 0, 0, 0, 0, -13, -85, 0, 0, 0, 0, -15, -85, 0, 0, 0, 0, -16, -88, 0, 0, 0, 0, -19, -90, 0, 0, 0, 0, -20, -85)
|
||||||
|
}
|
||||||
|
point_count = 30
|
||||||
|
|
||||||
|
[node name="Spray_Profile" type="Node" node_paths=PackedStringArray("Spray_Path")]
|
||||||
|
script = ExtResource("1_60v4d")
|
||||||
|
Spray_Noise = SubResource("FastNoiseLite_4msk4")
|
||||||
|
Spray_Path = NodePath("Path2D")
|
||||||
|
Path_Spray_Multiplier = 0.58
|
||||||
|
|
||||||
|
[node name="Path2D" type="Path2D" parent="."]
|
||||||
|
curve = SubResource("Curve2D_0vouc")
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://3mb12hvruajh"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dvcpsronqmkmr" path="res://client/template/Spawnable_Objects/SprayProfiles/spray_profile.gd" id="1_35g6m"]
|
||||||
|
|
||||||
|
[sub_resource type="FastNoiseLite" id="FastNoiseLite_4msk4"]
|
||||||
|
seed = 145
|
||||||
|
frequency = 0.307
|
||||||
|
cellular_jitter = -1.115
|
||||||
|
cellular_return_type = 0
|
||||||
|
domain_warp_type = 1
|
||||||
|
domain_warp_amplitude = 30.075
|
||||||
|
domain_warp_frequency = -0.835
|
||||||
|
domain_warp_fractal_type = 2
|
||||||
|
domain_warp_fractal_octaves = 1
|
||||||
|
|
||||||
|
[node name="Spray_Profile" type="Node"]
|
||||||
|
script = ExtResource("1_35g6m")
|
||||||
|
Spray_Noise = SubResource("FastNoiseLite_4msk4")
|
||||||
|
Random_Spray_Multiplier = 2.371
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://ceuaamuau8pl8"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dvcpsronqmkmr" path="res://client/template/Spawnable_Objects/SprayProfiles/spray_profile.gd" id="1_hmoot"]
|
||||||
|
|
||||||
|
[sub_resource type="FastNoiseLite" id="FastNoiseLite_4msk4"]
|
||||||
|
seed = 145
|
||||||
|
frequency = 0.307
|
||||||
|
cellular_jitter = -1.115
|
||||||
|
cellular_return_type = 0
|
||||||
|
domain_warp_type = 1
|
||||||
|
domain_warp_amplitude = 30.075
|
||||||
|
domain_warp_frequency = -0.835
|
||||||
|
domain_warp_fractal_type = 2
|
||||||
|
domain_warp_fractal_octaves = 1
|
||||||
|
|
||||||
|
[node name="Spray_Profile" type="Node"]
|
||||||
|
script = ExtResource("1_hmoot")
|
||||||
|
Spray_Noise = SubResource("FastNoiseLite_4msk4")
|
||||||
|
Random_Spray_Multiplier = 10.0
|
||||||
|
Updwards_Lock = true
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,65 @@
|
|||||||
|
[gd_scene load_steps=5 format=4 uid="uid://dxlkgbr1p580"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_bix2d"]
|
||||||
|
radius = 0.099605
|
||||||
|
height = 0.419677
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_x0fj0"]
|
||||||
|
resource_name = "yellow"
|
||||||
|
vertex_color_use_as_albedo = true
|
||||||
|
albedo_color = Color(0.0627451, 0.639216, 0.576471, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
emission_enabled = true
|
||||||
|
emission = Color(0, 0.960784, 0.960784, 1)
|
||||||
|
emission_energy_multiplier = 1.78
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_ly6dy"]
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.01, -0.025, -0.01, 0.02, 0.05, 0.02),
|
||||||
|
"format": 34359742465,
|
||||||
|
"index_count": 276,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIAAQAEAAMABAAFAAMABAAGAAUABgAHAAUABgAIAAcACAAJAAcACAAKAAkACgALAAkACgAMAAsADAANAAsADAAOAA0ADgAPAA0ADwAQAA0ADwARABAADwASABEAEgATABEAEgAUABMAFAAVABMAFAAWABUAFgAXABUAGQAYAAYABgAEABkAFgAUABoAGgAbABYAHAASAA8ADwAdABwAHgAFAAcABwAfAB4AIAAOAAwADAAhACAAEQATACIAIgAjABEAIQAMAAoACgAkACEAJwAlACYAJgAZACcAJgAoABkAKAAYABkAKAAeABgAHgApABgAHgAfACkAHwAkACkAHwAqACQAKgAhACQAKgArACEAKwAgACEAKwAsACAALAAdACAALAAtAB0ALQAcAB0ALQAjABwAIwAaABwAIwAiABoAIgAbABoAIgAuABsALgAvABsAKwALAA0ADQAsACsALAANABAAEAAtACwAHgAoAAMAAwAFAB4AJgAlAAAAAAACACYAJAAKAAgACAApACQAHwAHAAkACQAqAB8AJQAnAAEAAQAAACUALQAQABEAEQAjAC0AFAASABwAHAAaABQAFwAWABsAGwAvABcAEwAVAC4ALgAiABMAHQAPAA4ADgAgAB0AFQAXAC8ALwAuABUAJwAZAAQABAABACcAKQAIAAYABgAYACkAKAAmAAIAAgADACgAKgAJAAsACwArACoA"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 48,
|
||||||
|
"vertex_data": PackedByteArray("CtcjvM3MzLwAAACA3kEevM3MzLyhnik73kEevM3MzLyhnim7vOMNvM3MzLwK16O7vOMNvM3MzLwK16M7a7Tnu83MzLxrtOe7a7Tnu83MzLxrtOc7Cteju83MzLy84w28Cteju83MzLy84w08oZ4pu83MzLzeQR68oZ4pu83MzLzeQR48AAAAAM3MzLwK1yO8AAAAAM3MzLwK1yM8oZ4pO83MzLzeQR68oZ4pO83MzLzeQR48CtejO83MzLy84w08CtejO83MzLy84w28a7TnO83MzLxrtOe7a7TnO83MzLxrtOc7vOMNPM3MzLwK16O7vOMNPM3MzLwK16M73kEePM3MzLyhnim73kEePM3MzLyhnik7CtcjPM3MzLwAAACAa7Tnu83MzDxrtOc7vOMNvM3MzDwK16M7vOMNPM3MzDwK16M73kEePM3MzDyhnik7a7TnO83MzDxrtOc7CtejO83MzDy84w08a7Tnu83MzDxrtOe7Cteju83MzDy84w28oZ4pO83MzDzeQR48AAAAAM3MzDwK1yM8vOMNPM3MzDwK16O7a7TnO83MzDxrtOe7oZ4pu83MzDzeQR48CtcjvM3MzDwAAACA3kEevM3MzDyhnim73kEevM3MzDyhnik7vOMNvM3MzDwK16O7Cteju83MzDy84w08oZ4pu83MzDzeQR68AAAAAM3MzDwK1yO8oZ4pO83MzDzeQR68CtejO83MzDy84w283kEePM3MzDyhnim7CtcjPM3MzDwAAACA")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_sqgwv"]
|
||||||
|
resource_name = "foamBulletA_Mesh foamBulletA"
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.01, -0.025, -0.01, 0.02, 0.05, 0.02),
|
||||||
|
"attribute_data": PackedByteArray("JpPJPgAAgD/RtMI++AqNP9G0wj4Q6mU/o5GuPjabTT+jka4+ZTKZP/CIjj6Iuzg/8IiOPjyioz8mk0k+LrcoPyaTST5ppKs/g6/QPZilHj+Dr9A9NK2wPwAAAABtNhs/AAAAAMpksj+Dr9C9mKUeP4Ov0L00rbA/JpNJvmmkqz8mk0m+LrcoP/CIjr6Iuzg/8IiOvjyioz+jka6+NptNP6ORrr5lMpk/0bTCvhDqZT/RtMK++AqNPyaTyb4AAIA/gHxSPQACgTyAfFI9+Pv9P4B8Ur0AAoE8gHxSvfj7/T+AfFK9+Pv9P4B8Ur0AAoE8gHxSPfj7/T+AfFI9AAKBPIB8Uj34+/0/gHxSvfj7/T+AfFI9AAKBPIB8Ur0AAoE8gHxSPfj7/T+AfFK9+Pv9P4B8Uj0AAoE8gHxSvQACgTyAfFI9+Pv9P4B8Ur34+/0/gHxSPQACgTyAfFK9AAKBPIB8Ur34+/0/gHxSvQACgTyAfFI9+Pv9P4B8Uj0AAoE8gHxSPfj7/T+AfFK9+Pv9P4B8Uj0AAoE8gHxSvQACgTwmk8m+AACAP9G0wr4Q6mU/0bTCvvgKjT+jka6+ZTKZP6ORrr42m00/8IiOvjyioz/wiI6+iLs4PyaTSb5ppKs/JpNJvi63KD+Dr9C9NK2wP4Ov0L2YpR4/AAAAAMpksj8AAAAAbTYbP4Ov0D00rbA/g6/QPZilHj8mk0k+aaSrPyaTST4utyg/8IiOPjyioz/wiI4+iLs4P6ORrj5lMpk/o5GuPjabTT/RtMI++AqNP9G0wj4Q6mU/JpPJPgAAgD+AfFI9+Pv9P4B8Ur34+/0/gHxSPQACgTyAfFK9AAKBPIB8Uj34+/0/gHxSvfj7/T+AfFI9AAKBPIB8Ur0AAoE8gHxSPQACgTyAfFI9+Pv9P4B8Ur0AAoE8gHxSvfj7/T+AfFI9AAKBPIB8Uj34+/0/gHxSvQACgTyAfFK9+Pv9P4B8Uj34+/0/gHxSvfj7/T+AfFI9AAKBPIB8Ur0AAoE8gHxSPfj7/T+AfFK9+Pv9P4B8Uj0AAoE8gHxSvQACgTyAfFI9AAKBPIB8Uj34+/0/gHxSvQACgTyAfFK9+Pv9P4B8Uj34+/0/gHxSvfj7/T+AfFI9AAKBPIB8Ur0AAoE8gHxSvfj7/T+AfFK9AAKBPIB8Uj34+/0/gHxSPQACgTyAfFK9+Pv9P4B8Ur0AAoE8gHxSPfj7/T+AfFI9AAKBPIB8Ur34+/0/gHxSvQACgTyAfFI9+Pv9P4B8Uj0AAoE8gHxSPfj7/T+AfFK9+Pv9P4B8Uj0AAoE8gHxSvQACgTyAfFK9+Pv9P4B8Ur0AAoE8gHxSPfj7/T+AfFI9AAKBPIB8Uj0AAoE8gHxSPfj7/T+AfFK9AAKBPIB8Ur34+/0/gHxSPfj7/T+AfFK9+Pv9P4B8Uj0AAoE8gHxSvQACgTyAfFI9AAKBPIB8Uj34+/0/gHxSvQACgTyAfFK9+Pv9P4B8Uj34+/0/gHxSvfj7/T+AfFI9AAKBPIB8Ur0AAoE8"),
|
||||||
|
"format": 34359742487,
|
||||||
|
"index_count": 276,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIAAQAEAAMABAAFAAMABAAGAAUABgAHAAUABgAIAAcACAAJAAcACAAKAAkACgALAAkACgAMAAsADAANAAsADAAOAA0ADgAPAA0ADwAQAA0ADwARABAADwASABEAEgATABEAEgAUABMAFAAVABMAFAAWABUAFgAXABUAGgAYABkAGQAbABoAHgAcAB0AHQAfAB4AIgAgACEAIQAjACIAJgAkACUAJQAnACYAKgAoACkAKQArACoALgAsAC0ALQAvAC4AMgAwADEAMQAzADIANgA0ADUANQA3ADYANQA4ADcAOAA5ADcAOAA6ADkAOgA7ADkAOgA8ADsAPAA9ADsAPAA+AD0APgA/AD0APgBAAD8AQABBAD8AQABCAEEAQgBDAEEAQgBEAEMARABFAEMARABGAEUARgBHAEUARgBIAEcASABJAEcASABKAEkASgBLAEkATgBMAE0ATQBPAE4AUgBQAFEAUQBTAFIAVgBUAFUAVQBXAFYAWgBYAFkAWQBbAFoAXgBcAF0AXQBfAF4AYgBgAGEAYQBjAGIAZgBkAGUAZQBnAGYAagBoAGkAaQBrAGoAbgBsAG0AbQBvAG4AcgBwAHEAcQBzAHIAdgB0AHUAdQB3AHYAegB4AHkAeQB7AHoAfgB8AH0AfQB/AH4AggCAAIEAgQCDAIIAhgCEAIUAhQCHAIYAigCIAIkAiQCLAIoAjgCMAI0AjQCPAI4A"),
|
||||||
|
"material": SubResource("StandardMaterial3D_x0fj0"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 144,
|
||||||
|
"vertex_data": PackedByteArray("CtcjvM3MzLwAAACA3kEevM3MzLyhnik73kEevM3MzLyhnim7vOMNvM3MzLwK16O7vOMNvM3MzLwK16M7a7Tnu83MzLxrtOe7a7Tnu83MzLxrtOc7Cteju83MzLy84w28Cteju83MzLy84w08oZ4pu83MzLzeQR68oZ4pu83MzLzeQR48AAAAAM3MzLwK1yO8AAAAAM3MzLwK1yM8oZ4pO83MzLzeQR68oZ4pO83MzLzeQR48CtejO83MzLy84w08CtejO83MzLy84w28a7TnO83MzLxrtOe7a7TnO83MzLxrtOc7vOMNPM3MzLwK16O7vOMNPM3MzLwK16M73kEePM3MzLyhnim73kEePM3MzLyhnik7CtcjPM3MzLwAAACAa7Tnu83MzDxrtOc7a7Tnu83MzLxrtOc7vOMNvM3MzDwK16M7vOMNvM3MzLwK16M7vOMNPM3MzLwK16M7vOMNPM3MzDwK16M73kEePM3MzLyhnik73kEePM3MzDyhnik7a7TnO83MzLxrtOc7CtejO83MzLy84w08a7TnO83MzDxrtOc7CtejO83MzDy84w08a7Tnu83MzLxrtOe7Cteju83MzLy84w28a7Tnu83MzDxrtOe7Cteju83MzDy84w28oZ4pO83MzLzeQR48AAAAAM3MzLwK1yM8oZ4pO83MzDzeQR48AAAAAM3MzDwK1yM8vOMNPM3MzLwK16O7vOMNPM3MzDwK16O7a7TnO83MzLxrtOe7a7TnO83MzDxrtOe7AAAAAM3MzLwK1yM8oZ4pu83MzLzeQR48AAAAAM3MzDwK1yM8oZ4pu83MzDzeQR48CtcjvM3MzDwAAACA3kEevM3MzDyhnim73kEevM3MzDyhnik7vOMNvM3MzDwK16M7vOMNvM3MzDwK16O7a7Tnu83MzDxrtOc7a7Tnu83MzDxrtOe7Cteju83MzDy84w08Cteju83MzDy84w28oZ4pu83MzDzeQR48oZ4pu83MzDzeQR68AAAAAM3MzDwK1yM8AAAAAM3MzDwK1yO8oZ4pO83MzDzeQR48oZ4pO83MzDzeQR68CtejO83MzDy84w08CtejO83MzDy84w28a7TnO83MzDxrtOc7a7TnO83MzDxrtOe7vOMNPM3MzDwK16M7vOMNPM3MzDwK16O73kEePM3MzDyhnik73kEePM3MzDyhnim7CtcjPM3MzDwAAACAAAAAAM3MzLwK1yO8oZ4pO83MzLzeQR68AAAAAM3MzDwK1yO8oZ4pO83MzDzeQR68oZ4pO83MzLzeQR68CtejO83MzLy84w28oZ4pO83MzDzeQR68CtejO83MzDy84w28vOMNvM3MzDwK16O7vOMNvM3MzLwK16O7a7Tnu83MzDxrtOe7a7Tnu83MzLxrtOe7CtcjvM3MzDwAAACACtcjvM3MzLwAAACA3kEevM3MzDyhnim73kEevM3MzLyhnim7oZ4pu83MzLzeQR48Cteju83MzLy84w08oZ4pu83MzDzeQR48Cteju83MzDy84w08Cteju83MzLy84w28oZ4pu83MzLzeQR68Cteju83MzDy84w28oZ4pu83MzDzeQR683kEevM3MzDyhnik73kEevM3MzLyhnik7CtcjvM3MzDwAAACACtcjvM3MzLwAAACACtejO83MzLy84w28a7TnO83MzLxrtOe7CtejO83MzDy84w28a7TnO83MzDxrtOe7a7TnO83MzLxrtOc7a7TnO83MzDxrtOc7vOMNPM3MzLwK16M7vOMNPM3MzDwK16M73kEePM3MzLyhnik73kEePM3MzDyhnik7CtcjPM3MzLwAAACACtcjPM3MzDwAAACA3kEePM3MzLyhnim73kEePM3MzDyhnim7vOMNPM3MzLwK16O7vOMNPM3MzDwK16O7CtejO83MzLy84w08oZ4pO83MzLzeQR48CtejO83MzDy84w08oZ4pO83MzDzeQR48CtcjPM3MzLwAAACACtcjPM3MzDwAAACA3kEePM3MzLyhnim73kEePM3MzDyhnim7vOMNvM3MzDwK16M7vOMNvM3MzLwK16M73kEevM3MzDyhnik73kEevM3MzLyhnik7Cteju83MzLy84w08a7Tnu83MzLxrtOc7Cteju83MzDy84w08a7Tnu83MzDxrtOc73kEevM3MzDyhnim73kEevM3MzLyhnim7vOMNvM3MzDwK16O7vOMNvM3MzLwK16O7oZ4pu83MzLzeQR68AAAAAM3MzLwK1yO8oZ4pu83MzDzeQR68AAAAAM3MzDwK1yO8/38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//38AAAAA/7//fwAAAAD/v/9/AAAAAP+//z//f/+//7//P/9//7//v9ku/3/Zrv+/2S7/f9mu/78l0f9///+S6CXR/3///5Lo8uT/f///ePLy5P9///948v+//3/////f2a7/f///bNf/v/9/////39mu/3///2zXAAD/v/8//78AACXR2S7/vwAA/7//P/+/AAAl0dku/78Mm/9///+Fzf9//3////+/DJv/f///hc3/f/9/////v///2a4AAJLo///ZrgAAkuj///+/AAD/3////78AAP/f/3//f////7/yZP9/8uT/v/9//3////+/8mT/f/Lk/7//f///////v/9////+//+//3///////7//f///////v/9///////+//3///////7//f////v//v/9////+//+//3///////7//f///////v/9///////+//3///////7//f///////v/9////+//+//3////7//7//f///////v/9////+//+//3////7//7//f///////v/9///////+//3////7//7//f////v//v/9///////+//3///////7//////AAD/v///8uQAAIXN/////wAA/7////LkAACFzf//8uQAAIXN//8l0QAAbNf///LkAACFzf//JdEAAGzXAADZriVR/78AANmuJVH/vwAA/7//P/+/AAD/v/8//78AAP9//3//vwAA/3//f/+/AAAMm/Jk/78AAAyb8mT/v/Jk/3/y5P+/JVH/fyXR/7/yZP9/8uT/vyVR/38l0f+/AAAl0dku/78AAPLkDBv/vwAAJdHZLv+/AADy5Awb/78MG/9/DJv/vwwb/38Mm/+/AAD/f/9//78AAP9//3//v///JdEAAGzX////vwAA/9///yXRAABs1////78AAP/f/7//f////9//v/9/////3yXR/3///5LoJdH/f///kujy5P9///948vLk/3///3jy////f/////////9/////////DJsAAHjy//8MmwAAePL//9muAACS6P//2a4AAJLo2a7/f///bNcMm/9///+Fzdmu/3///2zXDJv/f///hc3///9//////////3////////8MmwAAePL//wybAAB48tku/3/Zrv+/2S7/f9mu/78MG/9/DJv/vwwb/38Mm/+/JVH/fyXR/7//P/9//7//vyVR/38l0f+//z//f/+//78AAAyb8mT/vwAADJvyZP+/AADZriVR/78AANmuJVH/vwAA8uQMG/+//////wAA/78AAPLkDBv/v/////8AAP+/")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
shadow_mesh = SubResource("ArrayMesh_ly6dy")
|
||||||
|
|
||||||
|
[node name="bullet" type="RigidBody3D"]
|
||||||
|
collision_layer = 16
|
||||||
|
collision_mask = 37
|
||||||
|
gravity_scale = 0.0
|
||||||
|
continuous_cd = true
|
||||||
|
contact_monitor = true
|
||||||
|
max_contacts_reported = 5
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0)
|
||||||
|
shape = SubResource("CapsuleShape3D_bix2d")
|
||||||
|
|
||||||
|
[node name="foamBulletA2" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(5, 0, 0, 0, -2.18557e-07, -5, 0, 5, -2.18557e-07, 0, 0, 0)
|
||||||
|
mesh = SubResource("ArrayMesh_sqgwv")
|
||||||
|
skeleton = NodePath("")
|
||||||
|
|
||||||
|
[connection signal="body_entered" from="." to="." method="_on_body_entered"]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://dguqnsdowqt3w"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://clfvflhvcccy1" path="res://client/template/Spawnable_Objects/Projectiles_To_Load/debug_sprite.png" id="1_smdbx"]
|
||||||
|
[ext_resource type="Script" uid="uid://dkt6w5wvebi72" path="res://client/template/scripts/Weapon_State_Machine/hit_debug.gd" id="2_gvqnn"]
|
||||||
|
|
||||||
|
[node name="Node3D" type="Sprite3D"]
|
||||||
|
transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 0, 0, 0)
|
||||||
|
billboard = 2
|
||||||
|
texture = ExtResource("1_smdbx")
|
||||||
|
script = ExtResource("2_gvqnn")
|
||||||
|
|
||||||
|
[node name="Timer" type="Timer" parent="."]
|
||||||
|
autostart = true
|
||||||
|
|
||||||
|
[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://bj15ogfi1535l"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dkt6w5wvebi72" path="res://client/template/scripts/Weapon_State_Machine/hit_debug.gd" id="2_w2l74"]
|
||||||
|
|
||||||
|
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_kr237"]
|
||||||
|
load_path = "res://.godot/imported/crosshair046.png-adf558f9d87ef20104f2ce22346ed1b7.s3tc.ctex"
|
||||||
|
|
||||||
|
[node name="Node3D" type="Sprite3D"]
|
||||||
|
modulate = Color(0, 0, 1, 1)
|
||||||
|
billboard = 2
|
||||||
|
texture = SubResource("CompressedTexture2D_kr237")
|
||||||
|
script = ExtResource("2_w2l74")
|
||||||
|
|
||||||
|
[node name="Timer" type="Timer" parent="."]
|
||||||
|
autostart = true
|
||||||
|
|
||||||
|
[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
[gd_scene load_steps=6 format=4 uid="uid://cqhlfy6ccomm3"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_030fb"]
|
||||||
|
resource_name = "yellow"
|
||||||
|
albedo_color = Color(0.996078, 0, 0.219608, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
emission_enabled = true
|
||||||
|
emission = Color(1, 0, 0, 1)
|
||||||
|
emission_energy_multiplier = 19.35
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_urtjv"]
|
||||||
|
resource_name = "pink"
|
||||||
|
albedo_color = Color(0.996078, 0.219608, 0.411765, 1)
|
||||||
|
metallic = 1.0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_iqbgl"]
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.01, -0.03, -0.01, 0.02, 0.06, 0.02),
|
||||||
|
"format": 34896613377,
|
||||||
|
"index_count": 66,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIAAQAAAAQABAAFAAEABQAEAAYABgAHAAUACAAHAAYABgAJAAgADAAKAAsACwANAAwADgAJAAYABgAKAA4ABgAEAAoABAALAAoABAAAAAsAAAACAAsADwAIAAkACQAOAA8ADwAOAAoACgAMAA8ACwACAAMAAwANAAsA"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 24,
|
||||||
|
"vertex_data": PackedByteArray("/98AAP/fAAD/31TV/98AAP//AAD/fwAA//9U1f9/AAD/fwAA//8AAP9/VNX//wAA/x8AAP/fAAD/H1TV/98AAAAAVNX/fwAAAAAAAP9/AAD/fwAAAAAAAP/fAAD/HwAA/39U1QAAAAD/31TV/x8AAP8fAAD/HwAA/x9U1f8fAAD/H////38AAP83////NwAA/zf////HAAD/f////98AAP9/////HwAA/8f////HAAD/x////zcAAP/f////fwAA")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.01, -0.03, -0.01, 0.02, 0.06, 0.02),
|
||||||
|
"format": 34896613377,
|
||||||
|
"index_count": 66,
|
||||||
|
"index_data": PackedByteArray("EgAQABEAEQATABIAEQAUABMAFAAVABMAFAAWABUAFgAXABUAFAAMAA0ADQAWABQAEQAPAAwADAAUABEAEwAFAAcABwASABMAEQAQAAgACAAPABEAEgAHAAgACAAQABIAFQABAAUABQATABUAAwABABUAFQAXAAMADQADABcAFwAWAA0A"),
|
||||||
|
"name": "pink",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0, 0, 0, 0),
|
||||||
|
"vertex_count": 24,
|
||||||
|
"vertex_data": PackedByteArray("/98AAP/fAAD/31TV/98AAP//AAD/fwAA//9U1f9/AAD/fwAA//8AAP9/VNX//wAA/x8AAP/fAAD/H1TV/98AAAAAVNX/fwAAAAAAAP9/AAD/fwAAAAAAAP/fAAD/HwAA/39U1QAAAAD/31TV/x8AAP8fAAD/HwAA/x9U1f8fAAD/H////38AAP83////NwAA/zf////HAAD/f////98AAP9/////HwAA/8f////HAAD/x////zcAAP/f////fwAA")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
|
||||||
|
[sub_resource type="ArrayMesh" id="ArrayMesh_mt61o"]
|
||||||
|
resource_name = "foamBulletB-low_Mesh foamBulletB-low"
|
||||||
|
_surfaces = [{
|
||||||
|
"aabb": AABB(-0.01, -0.03, -0.01, 0.02, 0.06, 0.02),
|
||||||
|
"attribute_data": PackedByteArray("SEP//0hDeYx5qP//eah5jLa8//+FV///trx5jIVXeYx5qP//SEP//3moeYxIQ3mMtrx5jLa8//+FV3mMhVf//3mo//9IQ///eah5jEhDeYz//666/98CzP/fWqn/f5Sj/3/J0f8fWqn/HwLMAACuunmoeYx5qP//SEN5jEhD//+2vP//hVf//7a8eYyFV3mMhVf//4VXeYy2vP//trx5jP8frrr/N6+t/zetx/9/Asz/f1qp/8etx//Hr63/3666eajHkkhDx5Jangh7dlIIe7a8x5KFV8eSiK0Ie6RhCHt5qMeSSEPHklqeCHt2Ugh7Wp4Ie3mox5J2Ugh7SEPHkra8x5KFV8eSiK0Ie6RhCHu2vMeShVfHkoitCHukYQh7SEPHknZSCHt5qMeSWp4Ie4VXx5KkYQh7trzHkoitCHs="),
|
||||||
|
"format": 34896613399,
|
||||||
|
"index_count": 66,
|
||||||
|
"index_data": PackedByteArray("AgAAAAEAAQADAAIABgAEAAUABQAHAAYACgAIAAkACQALAAoADgAMAA0ADQAPAA4AEgAQABEAEQATABIAFgAUABUAFQAXABYAFQAYABcAGAAZABcAGAAaABkAGgAbABkAHgAcAB0AHQAfAB4AIgAgACEAIQAjACIAJgAkACUAJQAnACYA"),
|
||||||
|
"material": SubResource("StandardMaterial3D_030fb"),
|
||||||
|
"name": "yellow",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0.787402, 4.3622, 0, 0),
|
||||||
|
"vertex_count": 80,
|
||||||
|
"vertex_data": PackedByteArray("/98AAP/f/5//31TV/9//n///AAD/f/+///9U1f9//7//3wAA/9//n/9/AAD//wCA/99U1f/f/5//f1TV//8AgP9/AAD//wCA/x8AAP/f/5//f1TV//8AgP8fVNX/3/+f/x9U1f/f/5//HwAA/9//nwAAVNX/f/+/AAAAAP9//7//fwAAAAD////fAAD/H//f/39U1QAA////31TV/x//3wAAAAD/f////x8AAP/f////HwAA/x////9/AAAAAP///38AAP//////3wAA/x/////fAAD/3/////8AAP9///8AAFTV/3//vwAAAAD/f/+//x9U1f8f/9//HwAA/x//3/8fAAD/H//f/38AAAAA////H1TV/x//3/9/VNUAAP////8AAP9//7///1TV/3//v//fAAD/H//f/99U1f8f/9//H////39U1f83////N1TV/zf////HVNX/f////99U1f9/////H1TV/8f////HVNX/x////zdU1f/f////f1TV/39U1QAA8vj/31TV/x/C6P9/////H/L4/8f///83wuj/H1TV/x/C6P9/VNUAAPL4/zf///83wuj/f////x/y+P9/VNX//5rA/x9U1f/feMb/f////9+awP83////x3jG/x////9/VNUAAFTV/39U1f83////N8Lo/x9U1f8fwuj/H1TV/994xgAAVNX/f1TV/zf////HeMb/H////39U1f/fVNX/33jG/39U1f//msD/x////8d4xv9/////35rA/99U1f/feMb/x////8d4xv//VNX/f1TV/9////9/VNX//1TV/39U1f/f////f1TV/99U1f8fwuj/x////zfC6P9/AAD/fwAA/38AAP9/AAD/fwAA/3////9/AAD/f////3////9/////f////3////9/////f////3////9/////f////38AAP9/////fwAA/3//P/9//z//f/8//3//P/9//z//f/8//3//P/9//z//f////3////9/////f////3////9/////f////3////9/AAD/fwAA/38AAP9/AAD///9/////f////3////9/////f////3////9/////f////3/oougi////f+ii6CLoohbd////f+iiFt3///9/////f+iiFt3///9/6KIW3ZmZZeaZmWXm6KIW3eiiFt3oohbdmZll5uiiFt2ZmWXm6KLoIv///3/oougi////f+ii6CLoougimZmZGZmZmRmZmZkZmZmZGeii6CLoougi")
|
||||||
|
}, {
|
||||||
|
"aabb": AABB(-0.01, -0.03, -0.01, 0.02, 0.06, 0.02),
|
||||||
|
"attribute_data": PackedByteArray("SEP//0hDeYx5qP//eah5jLa8//+FV///trx5jIVXeYx5qP//SEP//3moeYxIQ3mMtrx5jLa8//+FV3mMhVf//3mo//9IQ///eah5jEhDeYz//666/98CzP/fWqn/f5Sj/3/J0f8fWqn/HwLMAACuunmoeYx5qP//SEN5jEhD//+2vP//hVf//7a8eYyFV3mMhVf//4VXeYy2vP//trx5jP8frrr/N6+t/zetx/9/Asz/f1qp/8etx//Hr63/3666eajHkkhDx5Jangh7dlIIe7a8x5KFV8eSiK0Ie6RhCHt5qMeSSEPHklqeCHt2Ugh7Wp4Ie3mox5J2Ugh7SEPHkra8x5KFV8eSiK0Ie6RhCHu2vMeShVfHkoitCHukYQh7SEPHknZSCHt5qMeSWp4Ie4VXx5KkYQh7trzHkoitCHs="),
|
||||||
|
"format": 34896613399,
|
||||||
|
"index_count": 66,
|
||||||
|
"index_data": PackedByteArray("KgAoACkAKQArACoAKQAsACsALAAtACsALAAuAC0ALgAvAC0AMgAwADEAMQAzADIANgA0ADUANQA3ADYAOgA4ADkAOQA7ADoAPgA8AD0APQA/AD4AQgBAAEEAQQBDAEIARgBEAEUARQBHAEYASgBIAEkASQBLAEoATgBMAE0ATQBPAE4A"),
|
||||||
|
"material": SubResource("StandardMaterial3D_urtjv"),
|
||||||
|
"name": "pink",
|
||||||
|
"primitive": 3,
|
||||||
|
"uv_scale": Vector4(0.787402, 4.3622, 0, 0),
|
||||||
|
"vertex_count": 80,
|
||||||
|
"vertex_data": PackedByteArray("/98AAP/f/MX/31TV/9/8xf//AAD/f1TV//9U1f9/VNX/3wAA/9/8xf9/AAD//////99U1f/f/MX/f1TV//////9/AAD//////x8AAP/f/MX/f1TV//////8fVNX/3/zF/x9U1f/f/MX/HwAA/9/8xQAAVNX/f1TVAAAAAP9/VNX/fwAAAAD////fAAD/H6rp/39U1QAA////31TV/x+q6QAAAAD/f1TV/x8AAP/fVNX/HwAA/x9U1f9/AAAAAFTV/38AAP//VNX/3wAA/x9U1f/fAAD/31TV//8AAP9/VNUAAFTV/39U1QAAAAD/f1TV/x9U1f8fqun/HwAA/x+q6f8fAAD/H6rp/38AAAAA////H1TV/x+q6f9/VNUAAP////8AAP9/VNX//1TV/39U1f/fAAD/H6rp/99U1f8fqun/H////3//v/83////N/+//zf////H/7//f////9//v/9/////H/+//8f////H/7//x////zf/v//f////f/+//39U1QAAmP//31TV/x8E4f9/////H5j//8f///83BOH/H1TV/x8E4f9/VNUAAJj//zf///83BOH/f////x+Y//9/VNX//4CK/x9U1f/fWKH/f////9+Aiv83////x1ih/x////9/EcEAAFTV/38Rwf83////N63f/x9U1f8frd//H1TV/9+cogAAVNX/f0XA/zf////HnKL/H////39FwP/fVNX/31ih/39U1f//gIr/x////8dYof9/////34CK/99U1f/fnKL/x////8ecov//VNX/f0XA/9////9/RcD//1TV/38Rwf/f////fxHB/99U1f8frd//x////zet3/9/AAD/fwAA/38AAP9/AAD/fwAA/3//f/9/AAD/f/9//3//f/9/////f/9//3////9/////f////3////9/////f////38AAP9/////fwAAAAD/fwAA/38AAP9/AAD/fwAA/38AAP9/AAD/fwAA/3//f////3////9/////f////3////9/////f////3////9/AAD/fwAA/38AAP9/AAD+//9/////f/7//3////9/////f////3/+//9//v//f/uRcATFbRMB+5FwBMVtEwHsfsXtcIQD7ux+xe1whAPujd3Ggz6l8tyN3caDPqXy3HaIzuZ2iM7mYIyz6GCMs+iklPLT2pCb5qSU8tPakJvmDKM/JTj8jV0Moz8lOPyNXQuspBQLrKQUY5nbEGOZ2xAvmXcIL5l3CEqXYAxKl2AM")
|
||||||
|
}]
|
||||||
|
blend_shape_mode = 0
|
||||||
|
shadow_mesh = SubResource("ArrayMesh_iqbgl")
|
||||||
|
|
||||||
|
[sub_resource type="CylinderShape3D" id="CylinderShape3D_r5ds2"]
|
||||||
|
height = 0.202453
|
||||||
|
radius = 0.0673677
|
||||||
|
|
||||||
|
[node name="RigidBody3D" type="RigidBody3D"]
|
||||||
|
gravity_scale = 0.0
|
||||||
|
continuous_cd = true
|
||||||
|
contact_monitor = true
|
||||||
|
max_contacts_reported = 5
|
||||||
|
|
||||||
|
[node name="foamBulletB-low" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(10, 0, 0, 0, -4.37114e-07, 10, 0, -10, -4.37114e-07, 0, 0, 0)
|
||||||
|
mesh = SubResource("ArrayMesh_mt61o")
|
||||||
|
skeleton = NodePath("")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0)
|
||||||
|
shape = SubResource("CylinderShape3D_r5ds2")
|
||||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
|
|||||||
|
extends CanvasLayer
|
||||||
|
|
||||||
|
@onready var current_weapon_label = $debug_hud/HBoxContainer/CurrentWeapon
|
||||||
|
@onready var current_ammo_label = $debug_hud/HBoxContainer2/CurrentAmmo
|
||||||
|
@onready var current_weapon_stack = $debug_hud/HBoxContainer3/WeaponStack
|
||||||
|
@onready var hit_sight = $HitSight
|
||||||
|
@onready var hit_sight_timer = $HitSight/HitSightTimer
|
||||||
|
@onready var overLay = $Overlay
|
||||||
|
|
||||||
|
func _on_weapons_manager_update_weapon_stack(WeaponStack):
|
||||||
|
current_weapon_stack.text = ""
|
||||||
|
for i in WeaponStack:
|
||||||
|
current_weapon_stack.text += "\n"+i.weapon.weapon_name
|
||||||
|
|
||||||
|
func _on_weapons_manager_update_ammo(Ammo):
|
||||||
|
current_ammo_label.set_text(str(Ammo[0])+" / "+str(Ammo[1]))
|
||||||
|
|
||||||
|
func _on_weapons_manager_weapon_changed(WeaponName):
|
||||||
|
current_weapon_label.set_text(WeaponName)
|
||||||
|
|
||||||
|
func _on_hit_sight_timer_timeout():
|
||||||
|
hit_sight.set_visible(false)
|
||||||
|
|
||||||
|
func _on_weapons_manager_add_signal_to_hud(_projectile):
|
||||||
|
_projectile.Hit_Successfull.connect(_on_weapons_manager_hit_successfull)
|
||||||
|
|
||||||
|
func _on_weapons_manager_hit_successfull():
|
||||||
|
hit_sight.set_visible(true)
|
||||||
|
hit_sight_timer.start()
|
||||||
|
|
||||||
|
func load_over_lay_texture(Active:bool, txtr: Texture2D = null):
|
||||||
|
overLay.set_texture(txtr)
|
||||||
|
overLay.set_visible(Active)
|
||||||
|
|
||||||
|
func _on_weapons_manager_connect_weapon_to_hud(_weapon_resouce: WeaponResource):
|
||||||
|
_weapon_resouce.update_overlay.connect(load_over_lay_texture)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cc3t6vk7qpwey
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
extends CharacterBody3D
|
||||||
|
|
||||||
|
@onready var camera = %Camera
|
||||||
|
@export var subviewport_camera: Camera3D
|
||||||
|
@export var main_camera:Camera3D
|
||||||
|
@export var animation_tree: AnimationTree
|
||||||
|
|
||||||
|
var camera_rotation: Vector2 = Vector2(0.0,0.0)
|
||||||
|
var mouse_sensitivity = 0.001
|
||||||
|
var crouched: bool = false
|
||||||
|
var crouch_blocked: bool = false
|
||||||
|
|
||||||
|
@export_category("Crouch Parametres")
|
||||||
|
@export var enable_crouch: bool = true
|
||||||
|
@export var crouch_toggle: bool = false
|
||||||
|
@export var crouch_collision: ShapeCast3D
|
||||||
|
@export_range(0.0,3.0) var crouch_speed_reduction = 2.0
|
||||||
|
@export_range(0.0,0.50) var crouch_blend_speed = .2
|
||||||
|
enum {GROUND_CROUCH = -1, STANDING = 0, AIR_CROUCH = 1}
|
||||||
|
|
||||||
|
@export_category("Lean Parametres")
|
||||||
|
@export var enable_lean: bool = true
|
||||||
|
@export_range(0.0,1.0) var lean_speed: float = .2
|
||||||
|
@export var right_lean_collision: ShapeCast3D
|
||||||
|
@export var left_lean_collision: ShapeCast3D
|
||||||
|
var lean_tween
|
||||||
|
enum {LEFT = 1, CENTRE = 0, RIGHT = -1}
|
||||||
|
|
||||||
|
@export_category("speed Parameters")
|
||||||
|
@export var enable_sprint: bool = true
|
||||||
|
@export var sprint_timer: Timer
|
||||||
|
@export var sprint_cooldown_time: float = 3.0
|
||||||
|
@export var sprint_time: float = 1.0
|
||||||
|
@export var sprint_replenish_rate: float = 0.30
|
||||||
|
@export var acceleration: float = 120
|
||||||
|
@export_range(0.01,1.0) var air_acceleration_modifier: float = 0.1
|
||||||
|
var sprint_on_cooldown: bool = false
|
||||||
|
var sprint_time_remaining: float = sprint_time
|
||||||
|
@onready var sprint_bar: Range = $CanvasLayer/SprintBar
|
||||||
|
|
||||||
|
const NORMAL_speed = 1
|
||||||
|
@export_range(1.0,3.0) var sprint_speed: float = 2.0
|
||||||
|
@export_range(0.1,1.0) var walk_speed: float = 0.5
|
||||||
|
var speed_modifier: float = NORMAL_speed
|
||||||
|
|
||||||
|
@export_category("Jump Parameters")
|
||||||
|
@export var coyote_timer: Timer
|
||||||
|
@export var jump_peak_time: float = .5
|
||||||
|
@export var jump_fall_time: float = .5
|
||||||
|
@export var jump_height: float = 2.0
|
||||||
|
@export var jump_distance: float = 4.0
|
||||||
|
@export var coyote_time: float = .1
|
||||||
|
@export var jump_buffer_time: float = .2
|
||||||
|
|
||||||
|
# Get the gravity from the project settings to be synced with RigidBody nodes.
|
||||||
|
var jump_gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||||
|
var fall_gravity: float
|
||||||
|
var jump_velocity: float
|
||||||
|
var base_speed: float
|
||||||
|
var _speed: float
|
||||||
|
var jump_available: bool = true
|
||||||
|
var jump_buffer: bool = false
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
update_camera_rotation()
|
||||||
|
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||||
|
calculate_movement_parameters()
|
||||||
|
|
||||||
|
func update_camera_rotation() -> void:
|
||||||
|
var current_rotation = get_rotation()
|
||||||
|
camera_rotation.x = current_rotation.y
|
||||||
|
camera_rotation.y = current_rotation.x
|
||||||
|
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if event.is_action_pressed("ui_cancel"):
|
||||||
|
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||||
|
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||||
|
else:
|
||||||
|
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||||
|
|
||||||
|
if event is InputEventMouseMotion:
|
||||||
|
var MouseEvent = event.relative * mouse_sensitivity
|
||||||
|
camera_look(MouseEvent)
|
||||||
|
|
||||||
|
if enable_crouch:
|
||||||
|
if event.is_action_pressed("crouch"):
|
||||||
|
crouch()
|
||||||
|
if event.is_action_released("crouch"):
|
||||||
|
if !crouch_toggle and crouched:
|
||||||
|
crouch()
|
||||||
|
|
||||||
|
if enable_lean:
|
||||||
|
if Input.is_action_just_released("lean_left") or Input.is_action_just_released("lean_right"):
|
||||||
|
if !(Input.is_action_pressed("lean_right") or Input.is_action_pressed("lean_left")):
|
||||||
|
lean(CENTRE)
|
||||||
|
if Input.is_action_just_pressed("lean_left"):
|
||||||
|
lean(LEFT)
|
||||||
|
if Input.is_action_just_pressed("lean_right"):
|
||||||
|
lean(RIGHT)
|
||||||
|
|
||||||
|
if enable_sprint:
|
||||||
|
if Input.is_action_just_pressed("sprint") and !crouched:
|
||||||
|
if !sprint_on_cooldown:
|
||||||
|
speed_modifier = sprint_speed
|
||||||
|
sprint_timer.start(sprint_time_remaining)
|
||||||
|
|
||||||
|
if Input.is_action_just_released("sprint") or Input.is_action_just_released("walk"):
|
||||||
|
if !(Input.is_action_pressed("walk") or Input.is_action_pressed("sprint")):
|
||||||
|
speed_modifier = NORMAL_speed
|
||||||
|
exit_sprint()
|
||||||
|
|
||||||
|
if Input.is_action_just_pressed("walk") and !crouched:
|
||||||
|
speed_modifier = walk_speed
|
||||||
|
|
||||||
|
func calculate_movement_parameters() -> void:
|
||||||
|
jump_gravity = (2*jump_height)/pow(jump_peak_time,2)
|
||||||
|
fall_gravity = (2*jump_height)/pow(jump_fall_time,2)
|
||||||
|
jump_velocity = jump_gravity * jump_peak_time
|
||||||
|
base_speed = jump_distance/(jump_peak_time+jump_fall_time)
|
||||||
|
_speed = base_speed
|
||||||
|
|
||||||
|
func lean(blend_amount: int) -> void:
|
||||||
|
if is_on_floor():
|
||||||
|
if lean_tween:
|
||||||
|
lean_tween.kill()
|
||||||
|
|
||||||
|
lean_tween = get_tree().create_tween()
|
||||||
|
lean_tween.tween_property(animation_tree,"parameters/lean_blend/blend_amount", blend_amount, lean_speed)
|
||||||
|
|
||||||
|
func lean_collision() -> void:
|
||||||
|
animation_tree["parameters/left_collision_blend/blend_amount"] = lerp(
|
||||||
|
float(animation_tree["parameters/left_collision_blend/blend_amount"]),float(left_lean_collision.is_colliding()),lean_speed
|
||||||
|
)
|
||||||
|
animation_tree["parameters/right_collision_blend/blend_amount"] = lerp(
|
||||||
|
float(animation_tree["parameters/right_collision_blend/blend_amount"]),float(right_lean_collision.is_colliding()),lean_speed
|
||||||
|
)
|
||||||
|
|
||||||
|
func crouch() -> void:
|
||||||
|
var Blend
|
||||||
|
if !crouch_collision.is_colliding():
|
||||||
|
if crouched:
|
||||||
|
Blend = STANDING
|
||||||
|
else:
|
||||||
|
speed_modifier = NORMAL_speed
|
||||||
|
exit_sprint()
|
||||||
|
|
||||||
|
if is_on_floor():
|
||||||
|
Blend = GROUND_CROUCH
|
||||||
|
else:
|
||||||
|
Blend = AIR_CROUCH
|
||||||
|
var blend_tween = get_tree().create_tween()
|
||||||
|
blend_tween.tween_property(animation_tree,"parameters/Crouch_Blend/blend_amount",Blend,crouch_blend_speed)
|
||||||
|
crouched = !crouched
|
||||||
|
else:
|
||||||
|
crouch_blocked = true
|
||||||
|
|
||||||
|
func camera_look(Movement: Vector2) -> void:
|
||||||
|
camera_rotation += Movement
|
||||||
|
|
||||||
|
transform.basis = Basis()
|
||||||
|
camera.transform.basis = Basis()
|
||||||
|
|
||||||
|
rotate_object_local(Vector3(0,1,0),-camera_rotation.x) # first rotate in Y
|
||||||
|
camera.rotate_object_local(Vector3(1,0,0), -camera_rotation.y) # then rotate in X
|
||||||
|
camera_rotation.y = clamp(camera_rotation.y,-1.5,1.2)
|
||||||
|
|
||||||
|
func exit_sprint() -> void:
|
||||||
|
if !sprint_timer.is_stopped():
|
||||||
|
sprint_time_remaining = sprint_timer.time_left
|
||||||
|
sprint_timer.stop()
|
||||||
|
|
||||||
|
func sprint_replenish(delta) -> void:
|
||||||
|
var sprint_bar_Value
|
||||||
|
|
||||||
|
if !sprint_on_cooldown and (speed_modifier != sprint_speed):
|
||||||
|
|
||||||
|
if is_on_floor():
|
||||||
|
sprint_time_remaining = move_toward(sprint_time_remaining, sprint_time, delta*sprint_replenish_rate)
|
||||||
|
|
||||||
|
sprint_bar_Value= (sprint_time_remaining/sprint_time)*100
|
||||||
|
|
||||||
|
else:
|
||||||
|
sprint_bar_Value = (sprint_timer.time_left/sprint_time)*100
|
||||||
|
|
||||||
|
sprint_bar.value = sprint_bar_Value
|
||||||
|
|
||||||
|
if sprint_bar_Value == 100:
|
||||||
|
sprint_bar.hide()
|
||||||
|
else:
|
||||||
|
sprint_bar.show()
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
if subviewport_camera:
|
||||||
|
subviewport_camera.global_transform = main_camera.global_transform
|
||||||
|
|
||||||
|
|
||||||
|
func _physics_process(_delta: float) -> void:
|
||||||
|
sprint_replenish(_delta)
|
||||||
|
lean_collision()
|
||||||
|
|
||||||
|
if crouched and crouch_blocked:
|
||||||
|
if !crouch_collision.is_colliding():
|
||||||
|
crouch_blocked = false
|
||||||
|
if !Input.is_action_pressed("crouch") and !crouch_toggle:
|
||||||
|
crouch()
|
||||||
|
|
||||||
|
# Add the gravity.
|
||||||
|
var _acceleration
|
||||||
|
if not is_on_floor():
|
||||||
|
_acceleration = acceleration*air_acceleration_modifier
|
||||||
|
|
||||||
|
if coyote_timer.is_stopped():
|
||||||
|
coyote_timer.start(coyote_time)
|
||||||
|
|
||||||
|
if velocity.y>0:
|
||||||
|
velocity.y -= jump_gravity * _delta
|
||||||
|
else:
|
||||||
|
velocity.y -= fall_gravity * _delta
|
||||||
|
else:
|
||||||
|
_acceleration = acceleration
|
||||||
|
jump_available = true
|
||||||
|
coyote_timer.stop()
|
||||||
|
_speed = (base_speed / max((float(crouched)*crouch_speed_reduction),1)) * speed_modifier
|
||||||
|
if jump_buffer:
|
||||||
|
jump()
|
||||||
|
jump_buffer = false
|
||||||
|
# Handle Jump.
|
||||||
|
if Input.is_action_just_pressed("ui_accept"):
|
||||||
|
if jump_available:
|
||||||
|
if crouched:
|
||||||
|
crouch()
|
||||||
|
else:
|
||||||
|
lean(CENTRE)
|
||||||
|
jump()
|
||||||
|
else:
|
||||||
|
jump_buffer = true
|
||||||
|
get_tree().create_timer(jump_buffer_time).timeout.connect(on_jump_buffer_timeout)
|
||||||
|
|
||||||
|
# Get the input direction and handle the movement/deceleration.
|
||||||
|
# As good practice, you should replace UI actions with custom gameplay actions.
|
||||||
|
var input_dir = Input.get_vector("left", "right", "up", "down")
|
||||||
|
var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
|
||||||
|
velocity.x = move_toward(velocity.x, direction.x * _speed, _acceleration*_delta)
|
||||||
|
velocity.z = move_toward(velocity.z, direction.z * _speed, _acceleration*_delta)
|
||||||
|
|
||||||
|
move_and_slide()
|
||||||
|
|
||||||
|
func jump()->void:
|
||||||
|
velocity.y = jump_velocity
|
||||||
|
jump_available = false
|
||||||
|
|
||||||
|
func _on_sprint_timer_timeout() -> void:
|
||||||
|
sprint_on_cooldown = true
|
||||||
|
get_tree().create_timer(sprint_cooldown_time).timeout.connect(_on_sprint_cooldown_timeout)
|
||||||
|
speed_modifier = NORMAL_speed
|
||||||
|
sprint_time_remaining = 0
|
||||||
|
|
||||||
|
func _on_sprint_cooldown_timeout():
|
||||||
|
sprint_on_cooldown = false
|
||||||
|
|
||||||
|
func _on_coyote_timer_timeout() -> void:
|
||||||
|
jump_available = false
|
||||||
|
|
||||||
|
func on_jump_buffer_timeout()->void:
|
||||||
|
jump_buffer = false
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cfdt8t4r8pxel
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
extends Node3D
|
||||||
|
class_name Projectile
|
||||||
|
|
||||||
|
signal Hit_Successfull
|
||||||
|
|
||||||
|
## Can Be Either A Hit Scan or Rigid Body Projectile. If Rigid body is select a Rigid body must be provided.
|
||||||
|
@export_enum ("Hitscan","Rigidbody_Projectile","over_ride") var Projectile_Type: String = "Hitscan"
|
||||||
|
@export var Display_Debug_Decal: bool = true
|
||||||
|
|
||||||
|
@export_category("Rigid Body Projectile Properties")
|
||||||
|
@export var Projectile_Velocity: int
|
||||||
|
@export var Expirey_Time: int = 10
|
||||||
|
@export var Rigid_Body_Projectile: PackedScene
|
||||||
|
@export var pass_through: bool = false
|
||||||
|
|
||||||
|
@onready var Debug_Bullet = preload("res://client/template/Spawnable_Objects/hit_debug.tscn")
|
||||||
|
|
||||||
|
var damage: float = 0
|
||||||
|
var Projectiles_Spawned = []
|
||||||
|
var hit_objects: Array = []
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready() -> void:
|
||||||
|
get_tree().create_timer(Expirey_Time).timeout.connect(_on_timer_timeout)
|
||||||
|
|
||||||
|
func _Set_Projectile(_damage: int = 0,_spread:Vector2 = Vector2.ZERO, _Range: int = 1000, origin_point: Vector3 = Vector3.ZERO):
|
||||||
|
damage = _damage
|
||||||
|
Fire_Projectile(_spread,_Range,Rigid_Body_Projectile, origin_point)
|
||||||
|
|
||||||
|
func Fire_Projectile(_spread: Vector2 ,_range: int, _proj:PackedScene, origin_point: Vector3):
|
||||||
|
var Camera_Collision = Camera_Ray_Cast(_spread,_range)
|
||||||
|
|
||||||
|
match Projectile_Type:
|
||||||
|
"Hitscan":
|
||||||
|
Hit_Scan_Collision(Camera_Collision, damage,origin_point)
|
||||||
|
"Rigidbody_Projectile":
|
||||||
|
Launch_Rigid_Body_Projectile(Camera_Collision, _proj,origin_point)
|
||||||
|
"over_ride":
|
||||||
|
_over_ride_collision(Camera_Collision, damage)
|
||||||
|
|
||||||
|
func _over_ride_collision(_camera_collision:Array, _damage: float) -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
func Camera_Ray_Cast(_spread: Vector2 = Vector2.ZERO, _range: float = 1000):
|
||||||
|
var _Camera = get_viewport().get_camera_3d()
|
||||||
|
var _Viewport = get_viewport().get_size()
|
||||||
|
|
||||||
|
var Ray_Origin = _Camera.project_ray_origin(_Viewport/2)
|
||||||
|
var Ray_End = (Ray_Origin + _Camera.project_ray_normal((_Viewport/2)+Vector2i(_spread)) *_range)
|
||||||
|
var New_Intersection:PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(Ray_Origin,Ray_End)
|
||||||
|
New_Intersection.set_collision_mask(0b11101101)
|
||||||
|
New_Intersection.set_hit_from_inside(false) # In Jolt this is set to true by defualt
|
||||||
|
|
||||||
|
var Intersection = get_world_3d().direct_space_state.intersect_ray(New_Intersection)
|
||||||
|
|
||||||
|
if not Intersection.is_empty():
|
||||||
|
var Collision = [Intersection.collider,Intersection.position,Intersection.normal]
|
||||||
|
return Collision
|
||||||
|
else:
|
||||||
|
return [null,Ray_End,null]
|
||||||
|
|
||||||
|
func Hit_Scan_Collision(Collision: Array,_damage: float, origin_point: Vector3):
|
||||||
|
var Point = Collision[1]
|
||||||
|
if Collision[0]:
|
||||||
|
Load_Decal(Point, Collision[2])
|
||||||
|
|
||||||
|
if Collision[0].is_in_group("Target"):
|
||||||
|
var Bullet = get_world_3d().direct_space_state
|
||||||
|
|
||||||
|
var Bullet_Direction = (Point - origin_point).normalized()
|
||||||
|
var New_Intersection = PhysicsRayQueryParameters3D.create(origin_point,Point+Bullet_Direction*2)
|
||||||
|
New_Intersection.set_collision_mask(0b11101101)
|
||||||
|
New_Intersection.set_hit_from_inside(false)
|
||||||
|
New_Intersection.set_exclude(hit_objects)
|
||||||
|
var Bullet_Collision = Bullet.intersect_ray(New_Intersection)
|
||||||
|
|
||||||
|
if Bullet_Collision:
|
||||||
|
Hit_Scan_damage(Bullet_Collision.collider, Bullet_Direction,Bullet_Collision.position,_damage)
|
||||||
|
if pass_through and check_pass_through(Bullet_Collision.collider, Bullet_Collision.rid):
|
||||||
|
var pass_through_collision : Array = [Bullet_Collision.collider, Bullet_Collision.position, Bullet_Collision.normal]
|
||||||
|
var pass_through_damage: float = damage/2
|
||||||
|
Hit_Scan_Collision(pass_through_collision,pass_through_damage,Bullet_Collision.position)
|
||||||
|
return
|
||||||
|
queue_free()
|
||||||
|
|
||||||
|
func check_pass_through(collider: Node3D, rid: RID)-> bool:
|
||||||
|
var valid_pass_though: bool = false
|
||||||
|
if collider.is_in_group("Pass Through"):
|
||||||
|
hit_objects.append(rid)
|
||||||
|
valid_pass_though = true
|
||||||
|
return valid_pass_though
|
||||||
|
|
||||||
|
func Hit_Scan_damage(Collider, Direction, Position, _damage):
|
||||||
|
if Collider.is_in_group("Target") and Collider.has_method("Hit_Successful"):
|
||||||
|
Hit_Successfull.emit()
|
||||||
|
Collider.Hit_Successful(_damage, Direction, Position)
|
||||||
|
|
||||||
|
|
||||||
|
func Load_Decal(_pos,_normal):
|
||||||
|
if Display_Debug_Decal:
|
||||||
|
var rd = Debug_Bullet.instantiate()
|
||||||
|
var world = get_tree().get_root()
|
||||||
|
world.add_child(rd)
|
||||||
|
rd.global_translate(_pos+(_normal*.01))
|
||||||
|
|
||||||
|
func Launch_Rigid_Body_Projectile(Collision_Data, _projectile, _origin_point):
|
||||||
|
var _Point = Collision_Data[1]
|
||||||
|
var _Norm = Collision_Data[2]
|
||||||
|
var _proj : RigidBody3D = _projectile.instantiate()
|
||||||
|
_proj.position = _origin_point
|
||||||
|
|
||||||
|
var world = get_tree().get_first_node_in_group("World")
|
||||||
|
world.add_child(_proj)
|
||||||
|
|
||||||
|
_proj.look_at(_Point)
|
||||||
|
Projectiles_Spawned.push_back(_proj)
|
||||||
|
|
||||||
|
_proj.body_entered.connect(_on_body_entered.bind(_proj,_Norm))
|
||||||
|
|
||||||
|
var _Direction = (_Point - _origin_point).normalized()
|
||||||
|
_proj.set_linear_velocity(_Direction*Projectile_Velocity)
|
||||||
|
|
||||||
|
func _on_body_entered(body, _proj, _norm):
|
||||||
|
if body.is_in_group("Target") && body.has_method("Hit_Successful"):
|
||||||
|
body.Hit_Successful(damage)
|
||||||
|
Hit_Successfull.emit()
|
||||||
|
|
||||||
|
Load_Decal(_proj.get_position(),_norm)
|
||||||
|
_proj.queue_free()
|
||||||
|
|
||||||
|
Projectiles_Spawned.erase(_proj)
|
||||||
|
|
||||||
|
if Projectiles_Spawned.is_empty():
|
||||||
|
queue_free()
|
||||||
|
|
||||||
|
func _on_timer_timeout():
|
||||||
|
queue_free()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://b26eegme16mfw
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
extends Projectile
|
||||||
|
|
||||||
|
@onready var melee_hitbox: ShapeCast3D = $MeleeHitbox
|
||||||
|
|
||||||
|
func _over_ride_collision(_camera_collision:Array, _damage: float):
|
||||||
|
melee_hitbox.force_shapecast_update()
|
||||||
|
var colliders = melee_hitbox.get_collision_count()
|
||||||
|
for c in colliders:
|
||||||
|
var Target = melee_hitbox.get_collider(c)
|
||||||
|
if Target.is_in_group("Target") and Target.has_method("Hit_Successful"):
|
||||||
|
Hit_Successfull.emit()
|
||||||
|
var Direction = (Target.global_transform.origin - global_transform.origin).normalized()
|
||||||
|
var Position = melee_hitbox.get_collision_point(c)
|
||||||
|
Target.Hit_Successful(_damage, Direction, Position)
|
||||||
|
queue_free()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://bfn8eu00l4btq
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
extends Projectile
|
||||||
|
|
||||||
|
@export var explosion: ShapeCast3D
|
||||||
|
|
||||||
|
func _on_body_entered(_body, _proj, _norm):
|
||||||
|
var collision_location: Vector3 = _proj.global_position
|
||||||
|
explosion.global_position = collision_location
|
||||||
|
|
||||||
|
explosion.force_shapecast_update()
|
||||||
|
var targets = explosion.get_collision_count()
|
||||||
|
|
||||||
|
for t in targets:
|
||||||
|
var damage_target = explosion.get_collider(t)
|
||||||
|
var collision_point: Vector3 = explosion.get_collision_point(t)
|
||||||
|
var collision_normal: Vector3 = explosion.get_collision_normal(t)
|
||||||
|
var hit_scan_array = [damage_target,collision_point,collision_normal]
|
||||||
|
Hit_Scan_Collision(hit_scan_array,damage,collision_location)
|
||||||
|
|
||||||
|
_proj.queue_free()
|
||||||
|
Projectiles_Spawned.erase(_proj)
|
||||||
|
|
||||||
|
if Projectiles_Spawned.is_empty():
|
||||||
|
queue_free()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://biivm72fp7af8
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
extends Projectile
|
||||||
|
|
||||||
|
@onready var shotgun_pattern: Path2D = $shotgun_pattern
|
||||||
|
@export_range(0.0,20.0) var Randomness = 10.0
|
||||||
|
@export var Split_damage: bool = false
|
||||||
|
var Spray_Vector
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready() -> void:
|
||||||
|
Spray_Vector = shotgun_pattern.get_curve()
|
||||||
|
return super._ready()
|
||||||
|
|
||||||
|
func _Set_Projectile(_damage: int = 0,_spread:Vector2 = Vector2.ZERO, _Range: int = 1000, origin_point: Vector3 = Vector3.ZERO):
|
||||||
|
randomize()
|
||||||
|
damage = _damage/(max(Spray_Vector.get_point_count()*float(Split_damage),1))
|
||||||
|
|
||||||
|
for point in Spray_Vector.get_point_count():
|
||||||
|
var SprayPoint:Vector2 = Spray_Vector.get_point_position(point)
|
||||||
|
|
||||||
|
SprayPoint.x = SprayPoint.x + randf_range(-Randomness, Randomness)
|
||||||
|
SprayPoint.y = SprayPoint.y + randf_range(-Randomness, Randomness)
|
||||||
|
|
||||||
|
Fire_Projectile(SprayPoint,_Range,Rigid_Body_Projectile, origin_point)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dnh4a4vkl0cl1
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
extends RigidBody3D
|
||||||
|
class_name WeaponPickUp
|
||||||
|
|
||||||
|
@export var weapon: WeaponSlot
|
||||||
|
@export_enum("Weapon","Ammo") var TYPE = "Weapon"
|
||||||
|
|
||||||
|
var Pick_Up_Ready: bool = false
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
await get_tree().create_timer(2.0).timeout
|
||||||
|
Pick_Up_Ready = true
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://deaf6cmuofa0
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
extends RigidBody3D
|
||||||
|
|
||||||
|
@export var _weapon_name: String
|
||||||
|
@export var _current_ammo: int
|
||||||
|
@export var _reserve_ammo: int
|
||||||
|
|
||||||
|
const TYPE = "Ammo"
|
||||||
|
|
||||||
|
var Pick_Up_Ready: bool = false
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
await get_tree().create_timer(2.0).timeout
|
||||||
|
Pick_Up_Ready = true
|
||||||
|
|
||||||
|
func Set_Ammo(w_name: String, c_ammo : int, r_ammo : int):
|
||||||
|
if w_name == _weapon_name:
|
||||||
|
_current_ammo = c_ammo
|
||||||
|
_reserve_ammo = r_ammo
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dvxmf6dpx0qdn
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
extends Resource
|
||||||
|
class_name WeaponSlot
|
||||||
|
|
||||||
|
@export var weapon: WeaponResource
|
||||||
|
|
||||||
|
@export var current_ammo: int
|
||||||
|
@export var reserve_ammo: int
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://bw4j1fbww4k8s
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_resource type="Resource" script_class="WeaponResource" load_steps=3 format=3 uid="uid://ckwagka0u7opy"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ca7h24lxxrtvx" path="res://client/template/scripts/Weapon_State_Machine/weapon_resource.gd" id="1_2o2yi"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://6rfh1ejt4m6m" path="res://client/template/Spawnable_Objects/Projectiles_To_Load/melee_projectile.tscn" id="1_pf8fu"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_2o2yi")
|
||||||
|
weapon_name = "MeleeWeapon"
|
||||||
|
pick_up_animation = "blasterP_activate"
|
||||||
|
shoot_animation = "blasterP_strike"
|
||||||
|
reload_animation = ""
|
||||||
|
change_animation = "blasterP_deactivate"
|
||||||
|
drop_animation = ""
|
||||||
|
out_of_ammo_animation = ""
|
||||||
|
melee_animation = "blasterP_strike"
|
||||||
|
has_ammo = false
|
||||||
|
magazine = 0
|
||||||
|
max_ammo = 0
|
||||||
|
damage = 2
|
||||||
|
melee_damage = 1.0
|
||||||
|
auto_fire = true
|
||||||
|
fire_range = 1
|
||||||
|
can_be_dropped = false
|
||||||
|
projectile_to_load = ExtResource("1_pf8fu")
|
||||||
|
incremental_reload = false
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[gd_resource type="Resource" script_class="WeaponResource" format=3 uid="uid://yc1f2j7pl4vr"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ca7h24lxxrtvx" path="res://client/template/scripts/Weapon_State_Machine/weapon_resource.gd" id="1_jgxyc"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dc48o4niy1mok" path="res://client/template/Spawnable_Objects/Projectiles_To_Load/basic_hitscan_projectile.tscn" id="1_l4j8x"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cvbk5bxenpxey" path="res://client/template/Spawnable_Objects/Weapons/blaster_I.tscn" id="3_y2nwe"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ciu0xkaw0ioef" path="res://client/template/Spawnable_Objects/SprayProfiles/spray_profile_1.tscn" id="4_xc0or"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_jgxyc")
|
||||||
|
weapon_name = "blasterI"
|
||||||
|
pick_up_animation = "Global/blasterI Active"
|
||||||
|
shoot_animation = "Global/blasterI Shoot"
|
||||||
|
reload_animation = "Global/blasterI Reload"
|
||||||
|
change_animation = "Global/blasterI De-Activate"
|
||||||
|
drop_animation = "Global/blasterI Drop"
|
||||||
|
out_of_ammo_animation = "Global/blasterI OOA"
|
||||||
|
melee_animation = "Global/blasterI Melee"
|
||||||
|
magazine = 30
|
||||||
|
max_ammo = 60
|
||||||
|
damage = 10
|
||||||
|
melee_damage = 1.0
|
||||||
|
auto_fire = true
|
||||||
|
fire_range = 100
|
||||||
|
can_be_dropped = true
|
||||||
|
weapon_drop = ExtResource("3_y2nwe")
|
||||||
|
weapon_spray = ExtResource("4_xc0or")
|
||||||
|
projectile_to_load = ExtResource("1_l4j8x")
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
[gd_resource type="Resource" script_class="WeaponResource" load_steps=5 format=3 uid="uid://bwrs8ensewkgc"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bpxa3je174q43" path="res://client/template/Spawnable_Objects/Projectiles_To_Load/basic_rigid_body_projectile.tscn" id="1_1tllq"]
|
||||||
|
[ext_resource type="Script" uid="uid://ca7h24lxxrtvx" path="res://client/template/scripts/Weapon_State_Machine/weapon_resource.gd" id="2_gpxoe"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://gtthejj2wmyj" path="res://client/template/Spawnable_Objects/Weapons/blaster_n.tscn" id="3_5a5l8"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://3mb12hvruajh" path="res://client/template/Spawnable_Objects/SprayProfiles/spray_profile_2.tscn" id="4_p50xh"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("2_gpxoe")
|
||||||
|
weapon_name = "blasterN"
|
||||||
|
pick_up_animation = "Global/blasterL Activate"
|
||||||
|
shoot_animation = "Global/blasterL Shoot"
|
||||||
|
reload_animation = "Global/blasterL Reload"
|
||||||
|
change_animation = "Global/blasterL De-Activate"
|
||||||
|
drop_animation = "Global/blasterL Drop"
|
||||||
|
out_of_ammo_animation = "Global/blasterL OOA"
|
||||||
|
melee_animation = "Global/blasterL Melee"
|
||||||
|
has_ammo = true
|
||||||
|
magazine = 30
|
||||||
|
max_ammo = 60
|
||||||
|
damage = 1
|
||||||
|
melee_damage = 1.0
|
||||||
|
auto_fire = true
|
||||||
|
fire_range = 100
|
||||||
|
can_be_dropped = true
|
||||||
|
weapon_drop = ExtResource("3_5a5l8")
|
||||||
|
weapon_spray = ExtResource("4_p50xh")
|
||||||
|
projectile_to_load = ExtResource("1_1tllq")
|
||||||
|
incremental_reload = false
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
[gd_resource type="Resource" script_class="WeaponResource" load_steps=5 format=3 uid="uid://cd8msvjysh2uv"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ca7h24lxxrtvx" path="res://client/template/scripts/Weapon_State_Machine/weapon_resource.gd" id="1_4ro7h"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://brvfyahytwd8m" path="res://client/template/Spawnable_Objects/Projectiles_To_Load/shotgun_projectile.tscn" id="1_qi0jo"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://x8nhs2cer2to" path="res://client/template/Spawnable_Objects/Weapons/blaster_m.tscn" id="3_5hojr"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ceuaamuau8pl8" path="res://client/template/Spawnable_Objects/SprayProfiles/spray_profile_3.tscn" id="4_5jfrh"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_4ro7h")
|
||||||
|
weapon_name = "blasterM"
|
||||||
|
pick_up_animation = "blasterM_activate"
|
||||||
|
shoot_animation = "blasterM_Shoot"
|
||||||
|
reload_animation = "blasterM_Reload"
|
||||||
|
change_animation = "blasterM_Deactive"
|
||||||
|
drop_animation = "blasterM_Drop"
|
||||||
|
out_of_ammo_animation = "blasterM_OOA"
|
||||||
|
melee_animation = "blasterM_Melee"
|
||||||
|
has_ammo = true
|
||||||
|
magazine = 9
|
||||||
|
max_ammo = 36
|
||||||
|
damage = 5
|
||||||
|
melee_damage = 1.0
|
||||||
|
auto_fire = true
|
||||||
|
fire_range = 50
|
||||||
|
can_be_dropped = true
|
||||||
|
weapon_drop = ExtResource("3_5hojr")
|
||||||
|
weapon_spray = ExtResource("4_5jfrh")
|
||||||
|
projectile_to_load = ExtResource("1_qi0jo")
|
||||||
|
incremental_reload = true
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
[gd_resource type="Resource" script_class="WeaponResource" format=3 uid="uid://c1jg0ifn7yvve"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ca7h24lxxrtvx" path="res://client/template/scripts/Weapon_State_Machine/weapon_resource.gd" id="1_oxrhl"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dc48o4niy1mok" path="res://client/template/Spawnable_Objects/Projectiles_To_Load/basic_hitscan_projectile.tscn" id="1_xr7bm"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://gtthejj2wmyj" path="res://client/template/Spawnable_Objects/Weapons/blaster_n.tscn" id="3_c38hr"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://3mb12hvruajh" path="res://client/template/Spawnable_Objects/SprayProfiles/spray_profile_2.tscn" id="4_rk8dx"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_oxrhl")
|
||||||
|
weapon_name = "blaster_n"
|
||||||
|
pick_up_animation = "Global/blasterN Active"
|
||||||
|
shoot_animation = "Global/blasterN Shoot"
|
||||||
|
reload_animation = "Global/blasterN Reload"
|
||||||
|
change_animation = "Global/blasterN De-Activate"
|
||||||
|
drop_animation = "Global/blasterN Drop"
|
||||||
|
out_of_ammo_animation = "Global/blasterN OOA"
|
||||||
|
melee_animation = "Global/blasterN Melee"
|
||||||
|
magazine = 5
|
||||||
|
max_ammo = 15
|
||||||
|
damage = 1
|
||||||
|
melee_damage = 1.0
|
||||||
|
fire_range = 100
|
||||||
|
can_be_dropped = true
|
||||||
|
weapon_drop = ExtResource("3_c38hr")
|
||||||
|
weapon_spray = ExtResource("4_rk8dx")
|
||||||
|
projectile_to_load = ExtResource("1_xr7bm")
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[gd_resource type="Resource" script_class="WeaponResource" load_steps=4 format=3 uid="uid://dgayjkjx4mckc"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ca7h24lxxrtvx" path="res://client/template/scripts/Weapon_State_Machine/weapon_resource.gd" id="1_321c1"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ddfiivxgnakti" path="res://client/template/Spawnable_Objects/Projectiles_To_Load/rocket_launcher_projectile.tscn" id="1_xi5nj"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://dwgt7gwe3gfyy" path="res://client/template/Spawnable_Objects/Weapons/blasterQ.tscn" id="2_kih88"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_321c1")
|
||||||
|
weapon_name = "blasterQ"
|
||||||
|
pick_up_animation = "Global/blasterQ Active"
|
||||||
|
shoot_animation = "Global/blasterQ Shoot"
|
||||||
|
reload_animation = "Global/blasterQ Reload"
|
||||||
|
change_animation = "Global/blasterQ Deactivate"
|
||||||
|
drop_animation = "Global/blasterQ Drop"
|
||||||
|
out_of_ammo_animation = "Global/blasterQ OOA"
|
||||||
|
melee_animation = "Global/blasterQ Melee"
|
||||||
|
has_ammo = true
|
||||||
|
magazine = 2
|
||||||
|
max_ammo = 6
|
||||||
|
damage = 20
|
||||||
|
melee_damage = 2.0
|
||||||
|
auto_fire = false
|
||||||
|
fire_range = 100
|
||||||
|
can_be_dropped = true
|
||||||
|
weapon_drop = ExtResource("2_kih88")
|
||||||
|
projectile_to_load = ExtResource("1_xi5nj")
|
||||||
|
incremental_reload = false
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
extends Node3D
|
||||||
|
|
||||||
|
signal weapon_changed
|
||||||
|
signal update_ammo
|
||||||
|
signal update_weapon_stack
|
||||||
|
signal hit_successfull
|
||||||
|
signal add_signal_to_hud
|
||||||
|
|
||||||
|
signal connect_weapon_to_hud
|
||||||
|
|
||||||
|
@export var animation_player: AnimationPlayer
|
||||||
|
@export var melee_hitbox: ShapeCast3D
|
||||||
|
@export var max_weapons: int
|
||||||
|
@onready var bullet_point = get_node("%BulletPoint")
|
||||||
|
@onready var debug_bullet = preload("res://client/template/Spawnable_Objects/hit_debug.tscn")
|
||||||
|
|
||||||
|
var next_weapon: WeaponSlot
|
||||||
|
|
||||||
|
#The List of All Available weapons in the game
|
||||||
|
var spray_profiles: Dictionary = {}
|
||||||
|
var _count = 0
|
||||||
|
var shot_tween
|
||||||
|
@export var weapon_stack:Array[WeaponSlot] #An Array of weapons currently in possesion by the player
|
||||||
|
var current_weapon_slot: WeaponSlot = null
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if weapon_stack.is_empty():
|
||||||
|
push_error("Weapon Stack is empty, please populate with weapons")
|
||||||
|
else:
|
||||||
|
animation_player.animation_finished.connect(_on_animation_finished)
|
||||||
|
for i in weapon_stack:
|
||||||
|
initialize(i) #current starts on the first weapon in the stack
|
||||||
|
current_weapon_slot = weapon_stack[0]
|
||||||
|
if check_valid_weapon_slot():
|
||||||
|
enter()
|
||||||
|
update_weapon_stack.emit(weapon_stack)
|
||||||
|
|
||||||
|
func _unhandled_key_input(event: InputEvent) -> void:
|
||||||
|
if not event.is_pressed():
|
||||||
|
return
|
||||||
|
|
||||||
|
if range(KEY_1, KEY_4).has(event.keycode):
|
||||||
|
var _slot_number = (event.keycode - KEY_1)
|
||||||
|
if weapon_stack.size()-1>=_slot_number:
|
||||||
|
exit(weapon_stack[_slot_number])
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if event.is_action_pressed("WeaponUp"):
|
||||||
|
if check_valid_weapon_slot():
|
||||||
|
var weapon_index = weapon_stack.find(current_weapon_slot)
|
||||||
|
weapon_index = min(weapon_index+1,weapon_stack.size()-1)
|
||||||
|
exit(weapon_stack[weapon_index])
|
||||||
|
|
||||||
|
if event.is_action_pressed("WeaponDown"):
|
||||||
|
if check_valid_weapon_slot():
|
||||||
|
var weapon_index = weapon_stack.find(current_weapon_slot)
|
||||||
|
weapon_index = max(weapon_index-1,0)
|
||||||
|
exit(weapon_stack[weapon_index])
|
||||||
|
|
||||||
|
if event.is_action_pressed("Shoot"):
|
||||||
|
if check_valid_weapon_slot():
|
||||||
|
shoot()
|
||||||
|
|
||||||
|
if event.is_action_released("Shoot"):
|
||||||
|
if check_valid_weapon_slot():
|
||||||
|
shot_count_update()
|
||||||
|
|
||||||
|
if event.is_action_pressed("Reload"):
|
||||||
|
if check_valid_weapon_slot():
|
||||||
|
reload()
|
||||||
|
|
||||||
|
if event.is_action_pressed("Drop_Weapon"):
|
||||||
|
if check_valid_weapon_slot():
|
||||||
|
drop(current_weapon_slot)
|
||||||
|
|
||||||
|
if event.is_action_pressed("Melee"):
|
||||||
|
if check_valid_weapon_slot():
|
||||||
|
melee()
|
||||||
|
|
||||||
|
func check_valid_weapon_slot()->bool:
|
||||||
|
if current_weapon_slot:
|
||||||
|
if current_weapon_slot.weapon:
|
||||||
|
return true
|
||||||
|
else:
|
||||||
|
push_warning("No Weapon Resource active on the weapon controler.")
|
||||||
|
else:
|
||||||
|
push_warning("No Current Weapon slot active on the weapon controler.")
|
||||||
|
return false
|
||||||
|
|
||||||
|
func initialize(_weapon_slot: WeaponSlot):
|
||||||
|
if !_weapon_slot or !_weapon_slot.weapon:
|
||||||
|
return
|
||||||
|
if _weapon_slot.weapon.weapon_spray:
|
||||||
|
spray_profiles[_weapon_slot.weapon.weapon_name] = _weapon_slot.weapon.weapon_spray.instantiate()
|
||||||
|
connect_weapon_to_hud.emit(_weapon_slot.weapon)
|
||||||
|
|
||||||
|
func enter() -> void:
|
||||||
|
animation_player.queue(current_weapon_slot.weapon.pick_up_animation)
|
||||||
|
weapon_changed.emit(current_weapon_slot.weapon.weapon_name)
|
||||||
|
update_ammo.emit([current_weapon_slot.current_ammo, current_weapon_slot.reserve_ammo])
|
||||||
|
|
||||||
|
func exit(_next_weapon: WeaponSlot) -> void:
|
||||||
|
if _next_weapon != current_weapon_slot:
|
||||||
|
if animation_player.get_current_animation() != current_weapon_slot.weapon.change_animation:
|
||||||
|
animation_player.queue(current_weapon_slot.weapon.change_animation)
|
||||||
|
next_weapon = _next_weapon
|
||||||
|
|
||||||
|
func change_weapon(weapon_slot: WeaponSlot) -> void:
|
||||||
|
current_weapon_slot = weapon_slot
|
||||||
|
next_weapon = null
|
||||||
|
enter()
|
||||||
|
|
||||||
|
func shot_count_update() -> void:
|
||||||
|
shot_tween = get_tree().create_tween()
|
||||||
|
shot_tween.tween_property(self,"_count",0,1)
|
||||||
|
|
||||||
|
func shoot() -> void:
|
||||||
|
if current_weapon_slot.current_ammo != 0 or not current_weapon_slot.weapon.has_ammo:
|
||||||
|
if current_weapon_slot.weapon.incremental_reload and animation_player.current_animation == current_weapon_slot.weapon.reload_animation:
|
||||||
|
animation_player.stop()
|
||||||
|
|
||||||
|
if not animation_player.is_playing():
|
||||||
|
animation_player.play(current_weapon_slot.weapon.shoot_animation)
|
||||||
|
if current_weapon_slot.weapon.has_ammo:
|
||||||
|
current_weapon_slot.current_ammo -= 1
|
||||||
|
|
||||||
|
update_ammo.emit([current_weapon_slot.current_ammo, current_weapon_slot.reserve_ammo])
|
||||||
|
|
||||||
|
if shot_tween:
|
||||||
|
shot_tween.kill()
|
||||||
|
|
||||||
|
var Spread = Vector2.ZERO
|
||||||
|
|
||||||
|
if current_weapon_slot.weapon.weapon_spray:
|
||||||
|
_count = _count + 1
|
||||||
|
Spread = spray_profiles[current_weapon_slot.weapon.weapon_name].Get_Spray(_count, current_weapon_slot.weapon.magazine)
|
||||||
|
|
||||||
|
load_projectile(Spread)
|
||||||
|
else:
|
||||||
|
reload()
|
||||||
|
|
||||||
|
func load_projectile(_spread):
|
||||||
|
var _projectile:Projectile = current_weapon_slot.weapon.projectile_to_load.instantiate()
|
||||||
|
|
||||||
|
_projectile.position = bullet_point.global_position
|
||||||
|
_projectile.rotation = owner.rotation
|
||||||
|
|
||||||
|
bullet_point.add_child(_projectile)
|
||||||
|
add_signal_to_hud.emit(_projectile)
|
||||||
|
var bullet_point_origin = bullet_point.global_position
|
||||||
|
_projectile._Set_Projectile(current_weapon_slot.weapon.damage,_spread,current_weapon_slot.weapon.fire_range, bullet_point_origin)
|
||||||
|
|
||||||
|
func reload() -> void:
|
||||||
|
if current_weapon_slot.current_ammo == current_weapon_slot.weapon.magazine:
|
||||||
|
return
|
||||||
|
elif not animation_player.is_playing():
|
||||||
|
if current_weapon_slot.reserve_ammo != 0:
|
||||||
|
animation_player.queue(current_weapon_slot.weapon.reload_animation)
|
||||||
|
else:
|
||||||
|
animation_player.queue(current_weapon_slot.weapon.out_of_ammo_animation)
|
||||||
|
|
||||||
|
func calculate_reload() -> void:
|
||||||
|
if current_weapon_slot.current_ammo == current_weapon_slot.weapon.magazine:
|
||||||
|
var anim_legnth = animation_player.get_current_animation_length()
|
||||||
|
animation_player.advance(anim_legnth)
|
||||||
|
return
|
||||||
|
|
||||||
|
var Mag_Amount = current_weapon_slot.weapon.magazine
|
||||||
|
|
||||||
|
if current_weapon_slot.weapon.incremental_reload:
|
||||||
|
Mag_Amount = current_weapon_slot.current_ammo+1
|
||||||
|
|
||||||
|
var Reload_Amount = min(Mag_Amount-current_weapon_slot.current_ammo,Mag_Amount,current_weapon_slot.reserve_ammo)
|
||||||
|
|
||||||
|
current_weapon_slot.current_ammo = current_weapon_slot.current_ammo+Reload_Amount
|
||||||
|
current_weapon_slot.reserve_ammo = current_weapon_slot.reserve_ammo-Reload_Amount
|
||||||
|
|
||||||
|
update_ammo.emit([current_weapon_slot.current_ammo, current_weapon_slot.reserve_ammo])
|
||||||
|
shot_count_update()
|
||||||
|
|
||||||
|
func melee() -> void:
|
||||||
|
var Current_Anim = animation_player.get_current_animation()
|
||||||
|
|
||||||
|
if Current_Anim == current_weapon_slot.weapon.shoot_animation:
|
||||||
|
return
|
||||||
|
|
||||||
|
if Current_Anim != current_weapon_slot.weapon.melee_animation:
|
||||||
|
animation_player.play(current_weapon_slot.weapon.melee_animation)
|
||||||
|
if melee_hitbox.is_colliding():
|
||||||
|
var colliders = melee_hitbox.get_collision_count()
|
||||||
|
for c in colliders:
|
||||||
|
var Target = melee_hitbox.get_collider(c)
|
||||||
|
if Target.is_in_group("Target") and Target.has_method("Hit_Successful"):
|
||||||
|
hit_successfull.emit()
|
||||||
|
var Direction = (Target.global_transform.origin - owner.global_transform.origin).normalized()
|
||||||
|
var Position = melee_hitbox.get_collision_point(c)
|
||||||
|
Target.Hit_Successful(current_weapon_slot.weapon.melee_damage, Direction, Position)
|
||||||
|
|
||||||
|
func drop(_slot: WeaponSlot) -> void:
|
||||||
|
if _slot.weapon.can_be_dropped and weapon_stack.size() != 1:
|
||||||
|
var weapon_index = weapon_stack.find(_slot,0)
|
||||||
|
if weapon_index != -1:
|
||||||
|
weapon_stack.pop_at(weapon_index)
|
||||||
|
update_weapon_stack.emit(weapon_stack)
|
||||||
|
|
||||||
|
if _slot.weapon.weapon_drop:
|
||||||
|
var weapon_dropped = _slot.weapon.weapon_drop.instantiate()
|
||||||
|
weapon_dropped.weapon = _slot
|
||||||
|
weapon_dropped.set_global_transform(bullet_point.get_global_transform())
|
||||||
|
get_tree().get_root().add_child(weapon_dropped)
|
||||||
|
|
||||||
|
animation_player.play(current_weapon_slot.weapon.drop_animation)
|
||||||
|
weapon_index = max(weapon_index-1,0)
|
||||||
|
exit(weapon_stack[weapon_index])
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
func _on_animation_finished(anim_name):
|
||||||
|
if anim_name == current_weapon_slot.weapon.shoot_animation:
|
||||||
|
if current_weapon_slot.weapon.auto_fire == true:
|
||||||
|
if Input.is_action_pressed("Shoot"):
|
||||||
|
shoot()
|
||||||
|
|
||||||
|
if anim_name == current_weapon_slot.weapon.change_animation:
|
||||||
|
change_weapon(next_weapon)
|
||||||
|
|
||||||
|
if anim_name == current_weapon_slot.weapon.reload_animation:
|
||||||
|
if !current_weapon_slot.weapon.incremental_reload:
|
||||||
|
calculate_reload()
|
||||||
|
|
||||||
|
func _on_pick_up_detection_body_entered(body: RigidBody3D):
|
||||||
|
var weapon_slot = body.weapon
|
||||||
|
for slot in weapon_stack:
|
||||||
|
if slot.weapon == weapon_slot.weapon:
|
||||||
|
var remaining
|
||||||
|
|
||||||
|
remaining = add_ammo(slot, weapon_slot.current_ammo+weapon_slot.reserve_ammo)
|
||||||
|
weapon_slot.current_ammo = min(remaining, slot.weapon.magazine)
|
||||||
|
weapon_slot.reserve_ammo = max(remaining - weapon_slot.current_ammo,0)
|
||||||
|
|
||||||
|
if remaining == 0:
|
||||||
|
body.queue_free()
|
||||||
|
return
|
||||||
|
|
||||||
|
if body.TYPE == "Weapon":
|
||||||
|
if weapon_stack.size() == max_weapons:
|
||||||
|
return
|
||||||
|
|
||||||
|
if body.Pick_Up_Ready == true:
|
||||||
|
var weapon_index = weapon_stack.find(current_weapon_slot)
|
||||||
|
weapon_stack.insert(weapon_index,weapon_slot)
|
||||||
|
update_weapon_stack.emit(weapon_stack)
|
||||||
|
exit(weapon_slot)
|
||||||
|
initialize(weapon_slot)
|
||||||
|
body.queue_free()
|
||||||
|
|
||||||
|
func add_ammo(_weapon_slot: WeaponSlot, ammo: int)->int:
|
||||||
|
var weapon = _weapon_slot.weapon
|
||||||
|
var required = weapon.max_ammo - _weapon_slot.reserve_ammo
|
||||||
|
var remaining = max(ammo - required,0)
|
||||||
|
_weapon_slot.reserve_ammo += min(ammo, required)
|
||||||
|
update_ammo.emit([current_weapon_slot.current_ammo, current_weapon_slot.reserve_ammo])
|
||||||
|
return remaining
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cxxpp8cfxb8jp
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
extends RigidBody3D
|
||||||
|
|
||||||
|
signal Hit_Successfull
|
||||||
|
|
||||||
|
var damage: int = 0
|
||||||
|
|
||||||
|
func _on_body_entered(body):
|
||||||
|
if body.is_in_group("Target") && body.has_method("Hit_Successful"):
|
||||||
|
body.Hit_Successful(damage)
|
||||||
|
emit_signal("Hit_Successfull")
|
||||||
|
|
||||||
|
queue_free()
|
||||||
|
|
||||||
|
func _on_timer_timeout():
|
||||||
|
queue_free()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://11gya5082xv3
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
extends Sprite3D
|
||||||
|
|
||||||
|
func _on_timer_timeout():
|
||||||
|
queue_free()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dkt6w5wvebi72
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
@icon("res://client/template/scripts/Weapon_State_Machine/weapon_resource_icon.svg")
|
||||||
|
extends Resource
|
||||||
|
|
||||||
|
class_name WeaponResource
|
||||||
|
|
||||||
|
@warning_ignore("unused_signal")
|
||||||
|
signal update_overlay
|
||||||
|
@warning_ignore("unused_signal")
|
||||||
|
signal Zoom
|
||||||
|
|
||||||
|
@export_group("Weapon Animations")
|
||||||
|
##The Reference for the active weapons and pick ups
|
||||||
|
@export var weapon_name: String
|
||||||
|
## The Animation to play when the weapon was picked up
|
||||||
|
@export var pick_up_animation: String
|
||||||
|
## The Animation to play when the weapon is shot
|
||||||
|
@export var shoot_animation: String
|
||||||
|
## The Animation to play when the weapon reload
|
||||||
|
@export var reload_animation: String
|
||||||
|
## The Animation to play when the weapon is Changed
|
||||||
|
@export var change_animation: String
|
||||||
|
## The Animation to play when the weapon is dropped
|
||||||
|
@export var drop_animation: String
|
||||||
|
## The Animation to play when the weapon is out of ammo
|
||||||
|
@export var out_of_ammo_animation: String
|
||||||
|
## The Animation to play when you do the melee strike
|
||||||
|
@export var melee_animation: String
|
||||||
|
|
||||||
|
@export_group("Weapon Stats")
|
||||||
|
## If Uncheck Shoot Function Will skip ammo check
|
||||||
|
@export var has_ammo: bool = true
|
||||||
|
## The Maximum amount that you will reload if zero
|
||||||
|
@export var magazine: int
|
||||||
|
## The Maximum ammo that can be held in reserve.
|
||||||
|
@export var max_ammo: int
|
||||||
|
## The damage that a weapon will do
|
||||||
|
@export var damage: int
|
||||||
|
## The Melee damage that a weapon will do.
|
||||||
|
@export var melee_damage: float
|
||||||
|
## If Auto Fire is set to true the weapon will continuously fire until fire trigger is released
|
||||||
|
@export var auto_fire: bool
|
||||||
|
## The Range that a weapon will fire. Beyong this number no hit will trigger.
|
||||||
|
@export var fire_range: int
|
||||||
|
|
||||||
|
@export_group("Weapon Behaviour")
|
||||||
|
##If Checked the weapon drop scene MUST be provided
|
||||||
|
@export var can_be_dropped: bool
|
||||||
|
## The Rigid body to spawn for the weapon. It should be a Rigid Body of type Weapon_Pick_Up and have a matching weapon_name.
|
||||||
|
@export var weapon_drop: PackedScene
|
||||||
|
## The Spray_Profile to use when firing the weapon. It should be of Type Spray_Profile. This handles the spray calculations and passes back the information to the Projectile to load
|
||||||
|
@export var weapon_spray: PackedScene
|
||||||
|
## The Projectile that will be loaded. Not a Rigid body but class that handles the ray cast processing and can be either hitscan or rigid body. Should be of Type Projectile
|
||||||
|
@export var projectile_to_load: PackedScene
|
||||||
|
## Incremental Reload is for shotgun or sigle item loaded weapons where you can interupt the reload process. If true the Calculate_Reload function on the weapon_state_machine must be called indepently.
|
||||||
|
## For Example: at each step of a shotgun reload the function is called via the animation player.
|
||||||
|
@export var incremental_reload: bool = false
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://ca7h24lxxrtvx
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="120"
|
||||||
|
height="120"
|
||||||
|
viewBox="0 0 120 120"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:export-filename="Secondary/Pistol/Pistol_Icon.png"
|
||||||
|
inkscape:export-xdpi="51.200001"
|
||||||
|
inkscape:export-ydpi="51.200001"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="weapon_resource_icon.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="5.6568543"
|
||||||
|
inkscape:cx="80.786949"
|
||||||
|
inkscape:cy="57.275649"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1027"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer3"
|
||||||
|
showguides="false"><inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid1007" /></sodipodi:namedview><defs
|
||||||
|
id="defs2" /><g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="Pistol"><path
|
||||||
|
style="fill:#ffffff;stroke:#fdfdfd;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
|
||||||
|
d="M 38.383179,85.989654 H 22.544682 l 0.222115,-1.575826 1.386247,-0.721408 0.05719,-0.847949 -3.175572,-0.262031 -2.445375,-1.472439 -0.110658,-2.665305 2.949314,-7.791327 6.077368,-9.78273 2.91565,-6.907743 0.608662,-4.254202 -0.828252,-2.540647 -1.58005,-1.418767 -2.673229,-0.498449 -1.314259,-0.812357 0.201485,-1.760648 1.061372,-1.444114 v -7.969685 l 3.708858,0.113304 1.021667,-1.839682 h 1.253556 v 1.707812 h 63.130061 l 0.373363,-1.305175 h 1.081786 l 0.566864,1.261097 h 1.459724 v 2.15445 h 1.244851 v 5.163391 h -1.069317 v 6.063658 l -1.496296,1.496296 H 72.741608 l -1.470697,1.683184 -0.364041,1.454612 -0.04419,1.387817 v 2.164182 l 0.423966,1.778557 0.827681,2.201972 H 54.552451 l -2.623079,-0.958804 -2.411172,1.16365 -1.828793,4.83828 0.731728,2.413014 -2.39789,1.427434 -0.93345,2.930928 0.556376,2.135795 c 0,0 -2.498093,1.397347 -2.587302,1.586483 -0.08921,0.189136 -1.241396,3.928682 -1.241396,3.928682 l 0.08274,3.424204 -0.543105,1.129123 -2.746189,0.08859 0.0032,1.06841 h 1.282991 v 2.070652 z"
|
||||||
|
id="path1040"
|
||||||
|
inkscape:label="path1040" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
@@ -0,0 +1,18 @@
|
|||||||
|
[gd_resource type="Environment" format=3 uid="uid://bqp54syxqnqww"]
|
||||||
|
|
||||||
|
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_cotvt"]
|
||||||
|
sky_top_color = Color(0.160784, 0.482353, 0.827451, 1)
|
||||||
|
sky_horizon_color = Color(1, 1, 1, 1)
|
||||||
|
sun_angle_max = 114.44
|
||||||
|
|
||||||
|
[sub_resource type="Sky" id="Sky_prubo"]
|
||||||
|
sky_material = SubResource("ProceduralSkyMaterial_cotvt")
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
background_mode = 2
|
||||||
|
sky = SubResource("Sky_prubo")
|
||||||
|
sdfgi_use_occlusion = true
|
||||||
|
fog_light_color = Color(0.823529, 0.807843, 0.729412, 1)
|
||||||
|
fog_light_energy = 0.66
|
||||||
|
fog_sky_affect = 0.463
|
||||||
|
volumetric_fog_emission = Color(0.266667, 0.266667, 0.266667, 1)
|
||||||
@@ -1,87 +1,33 @@
|
|||||||
; Tactical Shooter — Default Server Configuration
|
; Default Server Configuration
|
||||||
; Godot ConfigFile format (INI-style). Edit this file to tune server behaviour.
|
; Override by creating config/server_config.cfg in the same directory.
|
||||||
; Load path: config://default_server_config.cfg (res:// path in Godot)
|
|
||||||
; Override at runtime by placing server_config.cfg next to the binary or via
|
|
||||||
; the SERVER_CFG environment variable.
|
|
||||||
;
|
|
||||||
; Sections marked [*] are defaults used at first run — they are written into
|
|
||||||
; the working-dir override if one doesn't exist, so the server always has
|
|
||||||
; a complete config.
|
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
; Human-readable server name shown in the browser / scoreboard.
|
port = 34197
|
||||||
server_name="Tactical Shooter Server"
|
max_players = 16
|
||||||
; Short description exposed to the server browser.
|
map = "test_range"
|
||||||
description="A tactical shooter server running on Godot 4 / ENet"
|
tickrate = 64
|
||||||
; Interface to bind to. Use "0.0.0.0" for all interfaces, "127.0.0.1" for local only.
|
round_time = 120
|
||||||
bind_ip="0.0.0.0"
|
freeze_time = 10
|
||||||
; Gameplay port. Default: 34197 (ephemeral range in IANA-ish neighbourhood).
|
bomb_timer = 40
|
||||||
port=34197
|
money_start = 800
|
||||||
; Maximum concurrent players. 2-32 recommended; ENet max is 4095 per host.
|
money_win = 3250
|
||||||
max_players=16
|
money_loss = 1900
|
||||||
; Server password. Empty = no password. Clients must supply this to connect.
|
money_kill = 300
|
||||||
password=""
|
money_plant = 300
|
||||||
; Simulation tick rate in Hz. Must match physics/common/physics_ticks_per_second.
|
money_defuse = 300
|
||||||
; The GDExtension core runs at this rate. 128 is standard for competitive FPS.
|
friendly_fire = false
|
||||||
tick_rate=128
|
enable_plugins = false
|
||||||
|
plugins_root = "res://server/plugins"
|
||||||
[game]
|
|
||||||
; Round time in seconds. 0 = unlimited. 600 = 10 minutes.
|
|
||||||
round_time_seconds=600
|
|
||||||
; Warmup period before round start, in seconds.
|
|
||||||
warmup_time_seconds=60
|
|
||||||
; Whether teammates can damage each other.
|
|
||||||
friendly_fire=false
|
|
||||||
; Damage multiplier applied to friendly fire damage (only if friendly_fire=true).
|
|
||||||
ff_damage_multiplier=0.5
|
|
||||||
; World gravity (negative = downward). Godot default is -9.8. Competitive FPS
|
|
||||||
; commonly uses -20 to -25 for snappier feel.
|
|
||||||
gravity=-24.0
|
|
||||||
; Respawn delay in seconds after a player dies.
|
|
||||||
respawn_time_seconds=5.0
|
|
||||||
; Whether dead players can spectate alive teammates.
|
|
||||||
spectate_enabled=true
|
|
||||||
|
|
||||||
[movement]
|
|
||||||
; Movement parameters exposed to the GDExtension SimulationServer.
|
|
||||||
; These match the config accepted by SimulationServer.set_movement_config().
|
|
||||||
walk_speed=5.0
|
|
||||||
sprint_speed=7.0
|
|
||||||
crouch_speed=2.0
|
|
||||||
jump_velocity=6.0
|
|
||||||
acceleration=20.0
|
|
||||||
air_acceleration=4.0
|
|
||||||
friction=8.0
|
|
||||||
|
|
||||||
[match]
|
|
||||||
; Rounds needed to win the match. Best-of-N means (2*win_limit-1) max rounds.
|
|
||||||
win_limit=3
|
|
||||||
; Match time limit in seconds. 0 = unlimited.
|
|
||||||
time_limit_seconds=0
|
|
||||||
; Map rotation mode: "sequence" (cycle in order), "vote" (player vote), "random".
|
|
||||||
map_rotation_mode="sequence"
|
|
||||||
; Comma-separated list of map scene resources to rotate through.
|
|
||||||
; Relative to the res://scenes/map/ directory.
|
|
||||||
; Example: "test_range, warehouse, compound"
|
|
||||||
maps="test_range"
|
|
||||||
|
|
||||||
[rcon]
|
[rcon]
|
||||||
; Remote console (admin access while server is running).
|
enabled = true
|
||||||
; This is wired to the future build:rcon system. Disabled until then.
|
port = 34198
|
||||||
enabled=false
|
password = "changeme"
|
||||||
; RCON password — must be non-empty and at least 8 chars when enabled.
|
|
||||||
password=""
|
|
||||||
; RCON listener port (typically different from game port).
|
|
||||||
port=34198
|
|
||||||
|
|
||||||
[logging]
|
[anticheat]
|
||||||
; Log level: "debug", "info", "warn", "error", "fatal".
|
enabled = true
|
||||||
log_level="info"
|
max_tick_rate = 130
|
||||||
; Log file path. Empty = stdout only. Relative paths are relative to the binary.
|
max_speed_multiplier = 1.5
|
||||||
log_file=""
|
aimbot_angle_threshold = 45.0
|
||||||
|
max_suspicion = 1.0
|
||||||
[teams]
|
auto_kick_threshold = 15
|
||||||
; Number of teams (2 for classic TDM, 4 for FFA squads, etc.).
|
|
||||||
team_count=2
|
|
||||||
; Maximum players per team. Total = team_count * players_per_team.
|
|
||||||
players_per_team=8
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"server_name": "Tactical Shooter Server",
|
||||||
|
"description": "",
|
||||||
|
"bind_ip": "0.0.0.0",
|
||||||
|
"port": 34197,
|
||||||
|
"max_players": 16,
|
||||||
|
"password": "",
|
||||||
|
"tick_rate": 128
|
||||||
|
},
|
||||||
|
"game": {
|
||||||
|
"round_time_seconds": 600,
|
||||||
|
"warmup_time_seconds": 60,
|
||||||
|
"friendly_fire": false,
|
||||||
|
"ff_damage_multiplier": 0.5,
|
||||||
|
"gravity": -24.0,
|
||||||
|
"respawn_time_seconds": 5.0,
|
||||||
|
"spectate_enabled": true
|
||||||
|
},
|
||||||
|
"movement": {
|
||||||
|
"walk_speed": 5.0,
|
||||||
|
"sprint_speed": 7.0,
|
||||||
|
"crouch_speed": 2.0,
|
||||||
|
"jump_velocity": 6.0,
|
||||||
|
"acceleration": 20.0,
|
||||||
|
"air_acceleration": 4.0,
|
||||||
|
"friction": 8.0
|
||||||
|
},
|
||||||
|
"match": {
|
||||||
|
"win_limit": 3,
|
||||||
|
"time_limit_seconds": 0,
|
||||||
|
"map_rotation_mode": "sequence",
|
||||||
|
"maps": "test_range"
|
||||||
|
},
|
||||||
|
"rcon": {
|
||||||
|
"enabled": true,
|
||||||
|
"password": "",
|
||||||
|
"port": 34198
|
||||||
|
},
|
||||||
|
"logging": {
|
||||||
|
"log_level": "info",
|
||||||
|
"log_file": ""
|
||||||
|
},
|
||||||
|
"teams": {
|
||||||
|
"team_count": 2,
|
||||||
|
"players_per_team": 8
|
||||||
|
}
|
||||||
|
}
|
||||||
+75
-178
@@ -1,204 +1,101 @@
|
|||||||
# Playtest Guide — Tactical Shooter
|
# Tactical Shooter — Playtest Guide
|
||||||
|
|
||||||
> Phase 0 dedicated server (headless ENet, 128-tick simulation, GDExtension core).
|
## Quick Start
|
||||||
> For internal / LAN playtesting only.
|
|
||||||
|
### 1. Get the Windows Client
|
||||||
|
|
||||||
|
Download `tactical-shooter-windows.zip` from the build directory, or clone the repo and find it at:
|
||||||
|
```
|
||||||
|
build/tactical-shooter-windows-x86_64/tactical-shooter.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
Extract the zip anywhere on your Windows machine and run `tactical-shooter.exe`.
|
||||||
|
|
||||||
|
### 2. Connect to the Test Server
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
|---------|-------|
|
||||||
|
| **Server IP** | `68.202.6.107` (external) or `192.168.0.127` (LAN) |
|
||||||
|
| **Port** | `34197` |
|
||||||
|
| **Protocol** | ENet (UDP) |
|
||||||
|
|
||||||
|
Launch the game, open the console (~) and type:
|
||||||
|
```
|
||||||
|
connect 68.202.6.107:34197
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Controls
|
||||||
|
|
||||||
|
| Action | Key |
|
||||||
|
|--------|-----|
|
||||||
|
| Move | W/A/S/D |
|
||||||
|
| Jump | Space |
|
||||||
|
| Sprint | Shift (tap to toggle) |
|
||||||
|
| Crouch | Ctrl (hold) |
|
||||||
|
| Shoot | Mouse LMB |
|
||||||
|
| Aim | Mouse RMB |
|
||||||
|
| Reload | R |
|
||||||
|
| Interact | E |
|
||||||
|
| Release mouse | Escape |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Server Address
|
## Dedicated Server Info
|
||||||
|
|
||||||
| Network | Address | Notes |
|
The test server runs on OPLabs infrastructure:
|
||||||
|---------|----------------|---------------------------------|
|
|
||||||
| Local | `127.0.0.1` | Run server + client on same box |
|
|
||||||
| LAN | `192.168.0.127`| Local network (eth0) |
|
|
||||||
| Tailscale | `100.89.190.113` | Tailnet (VPN-like, works from anywhere both peers have Tailscale) |
|
|
||||||
|
|
||||||
**Port:** `34197` (default — ENet gameplay port)
|
- **Host:** oplabs VM (192.168.0.127)
|
||||||
|
- **Binary:** `~/tactical-shooter-server/tactical-shooter-server.x86_64`
|
||||||
|
- **Service:** `systemctl --user status tactical-shooter-server`
|
||||||
|
- **Config:** `~/tactical-shooter-server/server_config.cfg`
|
||||||
|
|
||||||
Override via environment variable:
|
### Server Commands (SSH into oplabs)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
SERVER_PORT=34197 ./tactical-shooter-server.x86_64 --headless
|
# Check status
|
||||||
|
systemctl --user status tactical-shooter-server
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
journalctl --user -u tactical-shooter-server -f
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
systemctl --user restart tactical-shooter-server
|
||||||
|
|
||||||
|
# Stop
|
||||||
|
systemctl --user stop tactical-shooter-server
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### RCON Admin
|
||||||
|
|
||||||
## Building
|
Connect via TCP to port 34198 with the RCON password (set in server_config.cfg):
|
||||||
|
|
||||||
### Server (Linux, headless)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Export from Godot Editor (preset: "Linux Server"):
|
|
||||||
# Project → Export → Linux Server → Export Project
|
|
||||||
# Output: build/tactical-shooter-server.x86_64
|
|
||||||
|
|
||||||
# Or run from editor:
|
|
||||||
godot --headless --build-solutions
|
|
||||||
```
|
|
||||||
|
|
||||||
### Client (Windows)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Export from Godot Editor (preset: "Windows Client"):
|
|
||||||
# Project → Export → Windows Client → Export Project
|
|
||||||
# Output: build/tactical-shooter-windows-x86_64/tactical-shooter.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How to Launch
|
|
||||||
|
|
||||||
### 1. Start the Server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# On the host machine (Linux VPS / dev box):
|
|
||||||
./build/tactical-shooter-server.x86_64 --headless
|
|
||||||
```
|
|
||||||
|
|
||||||
The server logs its ready state:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
[ServerMain] Tactical Shooter server ready
|
rcon_password <password>
|
||||||
[ServerMain] Port: 34197
|
rcon commands
|
||||||
[ServerMain] Name: "Tactical Shooter Server"
|
rcon changelevel test_range
|
||||||
[ServerMain] Tick rate: 128 Hz
|
rcon kick <player_id>
|
||||||
[ServerMain] Maps: test_range
|
|
||||||
[ServerMain] Headless: True
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Stop with `Ctrl+C`.
|
|
||||||
|
|
||||||
### 2. Launch the Client
|
|
||||||
|
|
||||||
**Option A — Exported binary (Windows):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
.\tactical-shooter.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
The client auto-connects to `127.0.0.1:34197` by default. To connect to a remote server, set environment variables before launching:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# PowerShell
|
|
||||||
$env:SERVER_HOST = "192.168.0.127"
|
|
||||||
$env:SERVER_PORT = "34197"
|
|
||||||
.\tactical-shooter.exe
|
|
||||||
|
|
||||||
# CMD
|
|
||||||
set SERVER_HOST=192.168.0.127
|
|
||||||
set SERVER_PORT=34197
|
|
||||||
tactical-shooter.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
**Option B — Godot Editor (any OS):**
|
|
||||||
|
|
||||||
1. Open the project in Godot 4
|
|
||||||
2. Open `res://scenes/client/client_main.tscn`
|
|
||||||
3. Select the `ClientMain` node
|
|
||||||
4. Set `Server Host` to the server's IP (e.g. `192.168.0.127`)
|
|
||||||
5. Press **F6** (Play Scene) or **F5** (Run Project)
|
|
||||||
|
|
||||||
**Option C — Editor with environment overrides:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
SERVER_HOST=192.168.0.127 SERVER_PORT=34197 godot res://scenes/client/client_main.tscn
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Default Input Bindings
|
|
||||||
|
|
||||||
These are documented in `client/characters/input/input_handler.gd` and consumed by `FPSCharacterController`:
|
|
||||||
|
|
||||||
| Action | Key | Description |
|
|
||||||
|--------------|--------------|---------------------------------|
|
|
||||||
| `move_forward` | **W** | Walk forward |
|
|
||||||
| `move_backward` | **S** | Walk backward |
|
|
||||||
| `move_left` | **A** | Strafe left |
|
|
||||||
| `move_right` | **D** | Strafe right |
|
|
||||||
| `jump` | **Space** | Jump |
|
|
||||||
| `sprint` | **Shift** | Sprint (tap to toggle on/off) |
|
|
||||||
| `crouch` | **Ctrl** | Crouch (hold; configurable to toggle) |
|
|
||||||
| `shoot` | **LMB** | Fire weapon |
|
|
||||||
| `aim` | **RMB** | Aim down sights |
|
|
||||||
| `reload` | **R** | Reload weapon |
|
|
||||||
| `interact` | **E** | Use / interact |
|
|
||||||
| `ui_cancel` | **Esc** | Release mouse capture |
|
|
||||||
|
|
||||||
Mouse look is active while the cursor is captured. Click anywhere in the window to re-capture after pressing Esc.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Server Configuration
|
|
||||||
|
|
||||||
Edit `config/default_server_config.cfg` before building, or place a `server_config.cfg` next to the binary at runtime.
|
|
||||||
|
|
||||||
Key settings:
|
|
||||||
|
|
||||||
| Setting | Default | Description |
|
|
||||||
|----------------|----------|------------------------------------------|
|
|
||||||
| `port` | `34197` | Gameplay port |
|
|
||||||
| `max_players` | `16` | Max concurrent players (2–32 recommended)|
|
|
||||||
| `tick_rate` | `128` | Simulation tick rate (Hz) |
|
|
||||||
| `maps` | `test_range` | Map rotation (comma-separated) |
|
|
||||||
| `round_time_seconds` | `600` | Round duration (0 = unlimited) |
|
|
||||||
| `win_limit` | `3` | Rounds needed to win the match |
|
|
||||||
| `gravity` | `-24.0` | World gravity (competitive FPS setting) |
|
|
||||||
|
|
||||||
Override any setting at runtime:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./tactical-shooter-server.x86_64 --headless -- --port 34197 --max-players 16 --maps test_range
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Known Limitations (Phase 0)
|
|
||||||
|
|
||||||
This is a **foundations prototype**. The following are not yet implemented:
|
|
||||||
|
|
||||||
1. **No server browser** — join by IP only. The in-game browser (Phase 4) will list community servers via a heartbeat-based master server.
|
|
||||||
2. **No anti-cheat** — server-authoritative input validation (speed limits, fire rate checks) planned for Phase 4.
|
|
||||||
3. **No round system** — players spawn and move freely. Bomb-defuse / elimination / economy loop is Phase 2.
|
|
||||||
4. **No buy menu / economy** — no weapon purchasing. Only the default Assault Rifle hitscan weapon is available.
|
|
||||||
5. **Single map** (`test_range`) — the grey-box test environment. Map rotation supports multiple maps in config but only one exists.
|
|
||||||
6. **Client hardcodes `127.0.0.1`** — the client scene defaults to connecting to `127.0.0.1:34197`. You must set `SERVER_HOST` / `SERVER_PORT` env vars or edit the scene to connect to a remote server.
|
|
||||||
7. **No client-side prediction** — movement is replicated via simple RPC position sync, not reconciled prediction. Netcode will feel laggy under high ping. Client prediction + server reconciliation is Phase 1.
|
|
||||||
8. **No RCON** — remote admin console is disabled by default (`rcon.enabled=false`). Phase 4 adds TCP-based admin commands.
|
|
||||||
9. **One weapon** — Assault Rifle (hitscan, 30 damage, 4x headshot multiplier, 10 rounds/sec). No pistol, sniper, or utility items.
|
|
||||||
10. **Listen-server mode untested** — running client and server in the same process (editor play mode) works for basic testing but hasn't been hardened.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
| Symptom | Likely Cause | Fix |
|
| Issue | Fix |
|
||||||
|---------|-------------|-----|
|
|-------|-----|
|
||||||
| Client says `Connection failed` | Server not running or wrong IP/port | Verify server is running (`ps aux \| grep tactical`), check IP and port |
|
| Can't connect | Check server is running: `systemctl --user status tactical-shooter-server` |
|
||||||
| Client connects but sees no other players | Firewall blocking ENet port 34197 | Open UDP port 34197 on the server's firewall |
|
| Firewall block | Ensure port 34197/UDP is open on the server |
|
||||||
| `[ServerMain] Map scene not found` | Map path incorrect in config | Check `maps` setting in `config/default_server_config.cfg` |
|
| Game won't start | Install [VC++ Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe) |
|
||||||
| Server won't start / exits immediately | Port already in use | Change port in config or kill the process on port 34197 (`lsof -i :34197`) |
|
| High ping | The server is in eastern Canada; use the LAN IP if on the same network |
|
||||||
| `--headless` flag not recognised | Running outside Godot 4 | Ensure the binary was exported with dedicated server preset |
|
|
||||||
| Can't connect from another machine | Client still resolving to 127.0.0.1 | Pass `SERVER_HOST=<server-ip>` env var to client |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick-Start Cheat Sheet
|
## Architecture
|
||||||
|
|
||||||
```bash
|
```
|
||||||
# 1. Build server
|
[Windows Client] -- ENet/UDP :34197 --> [Dedicated Server (oplabs)]
|
||||||
godot --headless --export "Linux Server" build/tactical-shooter-server.x86_64
|
|
|
||||||
|
[GDExtension C++ Core]
|
||||||
# 2. Run server
|
[Game State 128Hz Tick]
|
||||||
./build/tactical-shooter-server.x86_64 --headless
|
|
||||||
|
|
||||||
# 3. Connect from client (same machine — new terminal)
|
|
||||||
SERVER_HOST=127.0.0.1 godot res://scenes/client/client_main.tscn
|
|
||||||
|
|
||||||
# 4. Connect from another machine on LAN
|
|
||||||
SERVER_HOST=192.168.0.127 godot res://scenes/client/client_main.tscn
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
See `docs/architecture.md` in the repo for full details.
|
||||||
|
|
||||||
*Last updated: July 2026*
|
|
||||||
|
|||||||
+356830
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
[configuration]
|
||||||
|
entry_symbol = "gdextension_entry"
|
||||||
|
compatibility_minimum = "4.2"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
linux.x86_64 = "res://gdextension/simulation/gdextension/bin/linux/libsimulation.so"
|
||||||
|
windows.x86_64 = ""
|
||||||
|
macos.universal = ""
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://c3r5nkuddarbb
|
||||||
Binary file not shown.
@@ -1,10 +0,0 @@
|
|||||||
[configuration]
|
|
||||||
entry_symbol = "gdextension_entry"
|
|
||||||
compatibility_minimum = "4.2"
|
|
||||||
|
|
||||||
[libraries]
|
|
||||||
linux.x86_64 = "res://gdextension/bin/linux/libsimulation.so"
|
|
||||||
linux.arm64 = "res://gdextension/bin/linux/libsimulation.so"
|
|
||||||
windows.x86_64 = "res://gdextension/bin/windows/libsimulation.dll"
|
|
||||||
macos.x86_64 = "res://gdextension/bin/macos/libsimulation.dylib"
|
|
||||||
macos.arm64 = "res://gdextension/bin/macos/libsimulation.dylib"
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user