feat: implement damage dummy system, floating UI numbers, and double-barrel shotgun mechanics
This commit is contained in:
@@ -9,6 +9,7 @@ var reload_timer: float = 0.0
|
||||
|
||||
var player: CharacterBody3D
|
||||
var camera: Camera3D
|
||||
var muzzle_flash: OmniLight3D
|
||||
|
||||
func _ready() -> void:
|
||||
set_process_input(true)
|
||||
@@ -52,6 +53,14 @@ func _build_model() -> void:
|
||||
barrel_r.rotation.x = deg_to_rad(90)
|
||||
barrel_r.position = Vector3(0.04, 0.03, -0.1)
|
||||
add_child(barrel_r)
|
||||
|
||||
# Muzzle Flash Light
|
||||
muzzle_flash = OmniLight3D.new()
|
||||
muzzle_flash.light_color = Color(1.0, 0.8, 0.3)
|
||||
muzzle_flash.light_energy = 0.0
|
||||
muzzle_flash.omni_range = 4.0
|
||||
muzzle_flash.position = Vector3(0, 0.03, -0.45)
|
||||
add_child(muzzle_flash)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if reloading:
|
||||
@@ -86,6 +95,8 @@ func _try_fire() -> void:
|
||||
shells -= 1
|
||||
print("BANG! Shells left: ", shells)
|
||||
_apply_impulse()
|
||||
_shoot_hitscan()
|
||||
_play_muzzle_flash()
|
||||
|
||||
# Start reload timer if we just shot the LAST shell
|
||||
if shells == 0:
|
||||
@@ -117,3 +128,50 @@ func _apply_impulse() -> void:
|
||||
if push_dir.y > 0:
|
||||
# Just slightly lift the player to break floor contact immediately
|
||||
player.position.y += 0.05
|
||||
|
||||
func _play_muzzle_flash() -> void:
|
||||
if muzzle_flash:
|
||||
muzzle_flash.light_energy = 8.0
|
||||
var tween = create_tween()
|
||||
tween.tween_property(muzzle_flash, "light_energy", 0.0, 0.1)
|
||||
|
||||
func _shoot_hitscan() -> void:
|
||||
if not camera: return
|
||||
|
||||
var space_state = camera.get_world_3d().direct_space_state
|
||||
var origin = camera.global_position
|
||||
var forward = -camera.global_transform.basis.z.normalized()
|
||||
var right = camera.global_transform.basis.x.normalized()
|
||||
var up = camera.global_transform.basis.y.normalized()
|
||||
|
||||
var num_pellets = 12
|
||||
var max_range = 50.0
|
||||
var base_dmg = 15.0
|
||||
var min_dmg = 3.0
|
||||
var falloff_start = 8.0
|
||||
var spread_angle = 0.08 # Radians
|
||||
|
||||
for i in range(num_pellets):
|
||||
# Random spread inside cone
|
||||
var r = sqrt(randf()) * spread_angle
|
||||
var theta = randf() * PI * 2.0
|
||||
var offset_dir = (right * cos(theta) + up * sin(theta)) * r
|
||||
var pellet_dir = (forward + offset_dir).normalized()
|
||||
|
||||
var target = origin + pellet_dir * max_range
|
||||
|
||||
var query = PhysicsRayQueryParameters3D.create(origin, target)
|
||||
if player:
|
||||
query.exclude = [player.get_rid()]
|
||||
|
||||
var result = space_state.intersect_ray(query)
|
||||
if result:
|
||||
var hit_dist = origin.distance_to(result.position)
|
||||
var damage = base_dmg
|
||||
|
||||
if hit_dist > falloff_start:
|
||||
var falloff_factor = clampf((hit_dist - falloff_start) / (max_range - falloff_start), 0.0, 1.0)
|
||||
damage = lerpf(base_dmg, min_dmg, falloff_factor)
|
||||
|
||||
if result.collider.has_method("take_damage"):
|
||||
result.collider.take_damage(damage, result.position, player)
|
||||
|
||||
Reference in New Issue
Block a user