feat: implement weapon framework, hitscan logic, and player movement controller with state management
This commit is contained in:
@@ -146,6 +146,9 @@ func _shoot_hitscan() -> void:
|
||||
|
||||
if result.collider.has_method("take_damage"):
|
||||
result.collider.take_damage(damage, result.position, player)
|
||||
else:
|
||||
# Not a target, spawn a bullet hole
|
||||
ImpactSpawner.spawn(get_tree(), "bullet", result.position, result.normal, 0.1)
|
||||
|
||||
# Add this hit to exclude so next loop goes through it
|
||||
var exc = query.exclude
|
||||
|
||||
@@ -121,6 +121,6 @@ func _shoot_projectile() -> void:
|
||||
|
||||
_spawn_custom_projectile(origin, fire_dir)
|
||||
|
||||
func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
|
||||
# Overridden by specific weapons to instance their unique visual projectile
|
||||
func _spawn_custom_projectile(_origin: Vector3, _fire_dir: Vector3) -> void:
|
||||
# Virtual method for custom projectiles (e.g., bouncing, homing)
|
||||
pass
|
||||
|
||||
@@ -195,6 +195,8 @@ func _shoot_hitscan() -> void:
|
||||
|
||||
if result.collider.has_method("take_damage"):
|
||||
result.collider.take_damage(damage, result.position, player)
|
||||
else:
|
||||
ImpactSpawner.spawn(get_tree(), "bullet", result.position, result.normal, 0.08)
|
||||
|
||||
# Spawn cosmetic tracer
|
||||
var tracer = Node3D.new()
|
||||
|
||||
@@ -7,6 +7,8 @@ var can_self_damage: bool = false
|
||||
var falloff_curve: float = 1.0 # 1.0 is linear, 2.0 is quadratic (steep drop)
|
||||
|
||||
func _on_hit(result: Dictionary) -> void:
|
||||
if not result.collider.has_method("take_damage"):
|
||||
ImpactSpawner.spawn(get_tree(), "crater", result.position, result.normal, explosion_radius * 1.5)
|
||||
_explode(result.position)
|
||||
queue_free()
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
extends Node
|
||||
class_name ImpactSpawner
|
||||
|
||||
static var bullet_hole_mat: StandardMaterial3D = null
|
||||
static var crater_mat: StandardMaterial3D = null
|
||||
static var plasma_mat: StandardMaterial3D = null
|
||||
|
||||
static func _ensure_mats():
|
||||
if bullet_hole_mat != null:
|
||||
return
|
||||
|
||||
var make_tex = func(colors: Array, offsets: Array, size: int) -> Texture2D:
|
||||
var g = Gradient.new()
|
||||
# For Godot 4 Gradient, we set offsets and colors directly
|
||||
var p_offsets = PackedFloat32Array()
|
||||
var p_colors = PackedColorArray()
|
||||
for o in offsets: p_offsets.append(o)
|
||||
for c in colors: p_colors.append(c)
|
||||
g.offsets = p_offsets
|
||||
g.colors = p_colors
|
||||
|
||||
var t = GradientTexture2D.new()
|
||||
t.gradient = g
|
||||
t.fill = GradientTexture2D.FILL_RADIAL
|
||||
t.fill_from = Vector2(0.5, 0.5)
|
||||
t.fill_to = Vector2(0.5, 0.0)
|
||||
t.width = size
|
||||
t.height = size
|
||||
return t
|
||||
|
||||
var tex_b = make_tex.call(
|
||||
[Color(0.01, 0.01, 0.01, 1.0), Color(0.1, 0.1, 0.1, 0.8), Color(0.1, 0.1, 0.1, 0.0)],
|
||||
[0.0, 0.3, 1.0], 32)
|
||||
bullet_hole_mat = StandardMaterial3D.new()
|
||||
bullet_hole_mat.albedo_texture = tex_b
|
||||
bullet_hole_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
|
||||
var tex_c = make_tex.call(
|
||||
[Color(0.05, 0.05, 0.05, 0.9), Color(0.1, 0.1, 0.1, 0.6), Color(0.1, 0.1, 0.1, 0.0)],
|
||||
[0.0, 0.5, 1.0], 128)
|
||||
crater_mat = StandardMaterial3D.new()
|
||||
crater_mat.albedo_texture = tex_c
|
||||
crater_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
|
||||
var tex_p = make_tex.call(
|
||||
[Color(0.8, 0.9, 1.0, 1.0), Color(0.2, 0.6, 1.0, 0.8), Color(0.1, 0.3, 0.8, 0.0)],
|
||||
[0.0, 0.2, 1.0], 64)
|
||||
plasma_mat = StandardMaterial3D.new()
|
||||
plasma_mat.albedo_texture = tex_p
|
||||
plasma_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
plasma_mat.emission_enabled = true
|
||||
plasma_mat.emission_texture = tex_p
|
||||
plasma_mat.emission_energy_multiplier = 2.0
|
||||
|
||||
static func spawn(scene_tree: SceneTree, type: String, pos: Vector3, normal: Vector3, size: float = 0.2):
|
||||
_ensure_mats()
|
||||
|
||||
var mi = MeshInstance3D.new()
|
||||
var qm = QuadMesh.new()
|
||||
qm.size = Vector2(size, size)
|
||||
|
||||
var mat: StandardMaterial3D
|
||||
if type == "bullet":
|
||||
mat = bullet_hole_mat.duplicate()
|
||||
elif type == "crater":
|
||||
mat = crater_mat.duplicate()
|
||||
elif type == "plasma":
|
||||
mat = plasma_mat.duplicate()
|
||||
|
||||
qm.material = mat
|
||||
mi.mesh = qm
|
||||
|
||||
scene_tree.current_scene.add_child(mi)
|
||||
|
||||
mi.global_position = pos + normal * 0.01
|
||||
|
||||
if abs(normal.dot(Vector3.UP)) < 0.999:
|
||||
mi.look_at(mi.global_position - normal, Vector3.UP)
|
||||
else:
|
||||
mi.look_at(mi.global_position - normal, Vector3.RIGHT)
|
||||
|
||||
var duration = 5.0 if type == "crater" else 3.0
|
||||
var tween = mi.create_tween()
|
||||
tween.tween_interval(duration)
|
||||
tween.tween_property(mat, "albedo_color:a", 0.0, 1.0)
|
||||
tween.tween_callback(mi.queue_free)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bf5h8cxi8kct2
|
||||
@@ -100,6 +100,7 @@ func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
|
||||
proj.max_range = max_range
|
||||
proj.drop_gravity = projectile_gravity
|
||||
proj.owner_player = player
|
||||
proj.impact_type = "plasma"
|
||||
|
||||
# Spawn in the world
|
||||
get_tree().current_scene.add_child(proj)
|
||||
|
||||
@@ -9,6 +9,7 @@ var owner_player: CharacterBody3D
|
||||
var falloff_start: float = 10.0
|
||||
var min_damage: float = 5.0
|
||||
var drop_gravity: float = 0.0 # For arc projectiles (nailgun might have slight drop, plasma none)
|
||||
var impact_type: String = "bullet"
|
||||
|
||||
var logical_source = null
|
||||
var _previous_position: Vector3
|
||||
@@ -58,6 +59,8 @@ func _on_hit(result: Dictionary) -> void:
|
||||
|
||||
if result.collider.has_method("take_damage"):
|
||||
result.collider.take_damage(final_damage, result.position, owner_player)
|
||||
else:
|
||||
var size = 0.4 if impact_type == "plasma" else 0.15
|
||||
ImpactSpawner.spawn(get_tree(), impact_type, result.position, result.normal, size)
|
||||
|
||||
# Spawn impact effect? Not needed yet for prototype.
|
||||
queue_free()
|
||||
|
||||
Reference in New Issue
Block a user