Feat/2 weapons foundation #6
@@ -82,7 +82,7 @@ func _build_environment() -> void:
|
|||||||
environment.sky = sky
|
environment.sky = sky
|
||||||
environment.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
environment.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
||||||
environment.ambient_light_energy = 0.4
|
environment.ambient_light_energy = 0.4
|
||||||
environment.tonemap_mode = 2 # Filmic
|
environment.tonemap_mode = Environment.TONE_MAPPER_FILMIC
|
||||||
environment.glow_enabled = true
|
environment.glow_enabled = true
|
||||||
environment.glow_intensity = 0.3
|
environment.glow_intensity = 0.3
|
||||||
environment.glow_bloom = 0.1
|
environment.glow_bloom = 0.1
|
||||||
|
|||||||
@@ -146,9 +146,9 @@ func apply_impulse(force: Vector3) -> void:
|
|||||||
# Tiny upward bump helps move_and_slide detach from the floor
|
# Tiny upward bump helps move_and_slide detach from the floor
|
||||||
global_position.y += 0.1
|
global_position.y += 0.1
|
||||||
|
|
||||||
func take_damage(amount: float, hit_pos: Vector3, source: Node = null) -> void:
|
func take_damage(amount: float, _hit_pos: Vector3, _source: Node3D = null) -> void:
|
||||||
# Show damage number for self-damage
|
# Show damage number for self-damage
|
||||||
spawn_damage_number(amount, hit_pos)
|
spawn_damage_number(amount, _hit_pos)
|
||||||
|
|
||||||
func _ensure_machine() -> MovementStateMachine:
|
func _ensure_machine() -> MovementStateMachine:
|
||||||
if is_instance_valid(_machine):
|
if is_instance_valid(_machine):
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ var params: MovementParams:
|
|||||||
get: return machine.params
|
get: return machine.params
|
||||||
|
|
||||||
var elapsed: float = 0.0
|
var elapsed: float = 0.0
|
||||||
var _initial_y_vel: float = 0.0
|
|
||||||
var _run_speed: float = 0.0
|
var _run_speed: float = 0.0
|
||||||
var _last_vel: Vector3 = Vector3.ZERO
|
var _last_vel: Vector3 = Vector3.ZERO
|
||||||
|
|
||||||
|
|||||||
@@ -146,6 +146,9 @@ func _shoot_hitscan() -> void:
|
|||||||
|
|
||||||
if result.collider.has_method("take_damage"):
|
if result.collider.has_method("take_damage"):
|
||||||
result.collider.take_damage(damage, result.position, player)
|
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
|
# Add this hit to exclude so next loop goes through it
|
||||||
var exc = query.exclude
|
var exc = query.exclude
|
||||||
|
|||||||
@@ -121,6 +121,6 @@ func _shoot_projectile() -> void:
|
|||||||
|
|
||||||
_spawn_custom_projectile(origin, fire_dir)
|
_spawn_custom_projectile(origin, fire_dir)
|
||||||
|
|
||||||
func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
|
func _spawn_custom_projectile(_origin: Vector3, _fire_dir: Vector3) -> void:
|
||||||
# Overridden by specific weapons to instance their unique visual projectile
|
# Virtual method for custom projectiles (e.g., bouncing, homing)
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -195,6 +195,8 @@ func _shoot_hitscan() -> void:
|
|||||||
|
|
||||||
if result.collider.has_method("take_damage"):
|
if result.collider.has_method("take_damage"):
|
||||||
result.collider.take_damage(damage, result.position, player)
|
result.collider.take_damage(damage, result.position, player)
|
||||||
|
else:
|
||||||
|
ImpactSpawner.spawn(get_tree(), "bullet", result.position, result.normal, 0.08)
|
||||||
|
|
||||||
# Spawn cosmetic tracer
|
# Spawn cosmetic tracer
|
||||||
var tracer = Node3D.new()
|
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)
|
var falloff_curve: float = 1.0 # 1.0 is linear, 2.0 is quadratic (steep drop)
|
||||||
|
|
||||||
func _on_hit(result: Dictionary) -> void:
|
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)
|
_explode(result.position)
|
||||||
queue_free()
|
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.max_range = max_range
|
||||||
proj.drop_gravity = projectile_gravity
|
proj.drop_gravity = projectile_gravity
|
||||||
proj.owner_player = player
|
proj.owner_player = player
|
||||||
|
proj.impact_type = "plasma"
|
||||||
|
|
||||||
# Spawn in the world
|
# Spawn in the world
|
||||||
get_tree().current_scene.add_child(proj)
|
get_tree().current_scene.add_child(proj)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ var owner_player: CharacterBody3D
|
|||||||
var falloff_start: float = 10.0
|
var falloff_start: float = 10.0
|
||||||
var min_damage: float = 5.0
|
var min_damage: float = 5.0
|
||||||
var drop_gravity: float = 0.0 # For arc projectiles (nailgun might have slight drop, plasma none)
|
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 logical_source = null
|
||||||
var _previous_position: Vector3
|
var _previous_position: Vector3
|
||||||
@@ -58,6 +59,8 @@ func _on_hit(result: Dictionary) -> void:
|
|||||||
|
|
||||||
if result.collider.has_method("take_damage"):
|
if result.collider.has_method("take_damage"):
|
||||||
result.collider.take_damage(final_damage, result.position, owner_player)
|
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()
|
queue_free()
|
||||||
|
|||||||
Reference in New Issue
Block a user