Weapons no longer reload by spinning the gun 360 (that was the upside-down
flip; nothing detached). New ViewmodelAnim module drives per-style manual-of-
arms sequences on the existing model_root + named arm pivots, with a temporary
magazine/shell/rocket/cell prop that visibly leaves and returns to the gun:
- mag (M4/AK/MP7/DMR): roll gun to present the well, hand rips the mag
down and away, fresh mag up, seat, charge. Gun stays UPRIGHT.
- boltmag (AWP): mag swap + a distinct bolt-cycle rock.
- break (double barrel): hinge open, flick both shells out, drop fresh
shells in, snap shut.
- tube (rocket launcher): tip the tube in, shove a rocket home, shoulder.
- cell (plasma/nail gun): glowing energy cell swapped on the side.
Per-shot viewmodel recoil kick (ViewmodelAnim.apply_kick) scaled by each
weapon's recoil_amplitude, so an AWP slams and an MP7 chatters — replaces the
old flat model behaviour. Weapons expose reload_style; base classes call
play_reload / stop (on unequip) / apply_kick.
Knife: the old barely-visible flick is now a real diagonal slash that
alternates backhand/forehand, whips the blade through screen centre, and
fires the third-person melee arm-swing action (synced to other players).
Hair/materials: specular fully to 0 (even a 2% stepped glint swept as shine
across Taila's hair when the camera moved); rim trimmed to a whisper. Fully
matte cloth/hair now.
Verify: debug/fp_weapon_capture.gd screenshots each weapon's reload phases,
the knife slash, and the fire kick.
Co-Authored-By: Claude Fable 5 <[email protected]>
98 lines
2.9 KiB
GDScript
98 lines
2.9 KiB
GDScript
extends BaseProjectileWeapon
|
|
class_name PlasmaGun
|
|
|
|
func _init() -> void:
|
|
weapon_name = "Plasma Gun"
|
|
reload_style = "cell"
|
|
fire_rate = 0.15 # Moderate
|
|
max_ammo = 25
|
|
reload_time = 1.8
|
|
base_damage = 50.0
|
|
min_damage = 50.0 # No falloff
|
|
falloff_start = 0.0 # Unused because min=base
|
|
max_range = 300.0
|
|
automatic = true
|
|
spread_angle = 0.005 # Highly accurate
|
|
|
|
projectile_speed = 80.0
|
|
projectile_gravity = 0.0
|
|
|
|
func _build_model() -> void:
|
|
position = Vector3(0.3, -0.3, -0.656)
|
|
|
|
var model_scene = load("res://assets/weapons/plasmagun/source/Plasma rifle.fbx")
|
|
if model_scene:
|
|
var model = model_scene.instantiate()
|
|
model_root.add_child(model)
|
|
model.scale = Vector3(1.0, 1.0, 1.0)
|
|
model.rotation_degrees.y = 180
|
|
for light in model.find_children("*", "Light3D", true, false):
|
|
light.queue_free()
|
|
|
|
var mat = StandardMaterial3D.new()
|
|
mat.albedo_texture = load("res://assets/weapons/plasmagun/textures/Plasma_rifle.png")
|
|
|
|
for child in model.find_children("*", "MeshInstance3D", true, false):
|
|
child.set_surface_override_material(0, mat)
|
|
|
|
# Muzzle Flash
|
|
muzzle_flash = OmniLight3D.new()
|
|
muzzle_flash.light_color = Color(0.2, 0.5, 1.0)
|
|
muzzle_flash.light_energy = 0.0
|
|
muzzle_flash.omni_range = 6.0
|
|
muzzle_flash.position = Vector3(0, 0.02, -0.4)
|
|
model_root.add_child(muzzle_flash)
|
|
|
|
# Audio
|
|
fire_sound = AudioStreamPlayer3D.new()
|
|
fire_sound.bus = "SFX"
|
|
fire_sound.stream = AudioManager.stream_for("plasma_fire", "res://assets/sounds/plasma_fire.wav")
|
|
fire_sound.position = muzzle_flash.position
|
|
fire_sound.volume_db = -5.0
|
|
model_root.add_child(fire_sound)
|
|
|
|
reload_sound = AudioStreamPlayer3D.new()
|
|
reload_sound.bus = "SFX"
|
|
reload_sound.stream = AudioManager.stream_for("shotgun_reload", "res://assets/sounds/shotgun_reload.wav")
|
|
model_root.add_child(reload_sound)
|
|
|
|
func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
|
|
var proj = load("res://weapons/projectile.gd").new()
|
|
|
|
# Give it a glowing blue mesh
|
|
var mesh_instance = MeshInstance3D.new()
|
|
var p_mesh = SphereMesh.new()
|
|
p_mesh.radius = 0.08
|
|
p_mesh.height = 0.16
|
|
var mat = StandardMaterial3D.new()
|
|
mat.albedo_color = Color(0.2, 0.6, 1.0)
|
|
mat.emission_enabled = true
|
|
mat.emission = Color(0.2, 0.6, 1.0)
|
|
mat.emission_energy_multiplier = 5.0
|
|
p_mesh.material = mat
|
|
mesh_instance.mesh = p_mesh
|
|
proj.add_child(mesh_instance)
|
|
|
|
var light = OmniLight3D.new()
|
|
light.light_color = Color(0.2, 0.6, 1.0)
|
|
proj.add_child(light)
|
|
|
|
proj.position = muzzle_flash.global_position if muzzle_flash else origin
|
|
proj.logical_source = origin
|
|
|
|
proj.velocity = fire_dir * projectile_speed
|
|
if player:
|
|
proj.velocity += fire_dir * max(0.0, player.velocity.dot(fire_dir))
|
|
proj.damage = base_damage
|
|
proj.min_damage = min_damage
|
|
proj.falloff_start = falloff_start
|
|
proj.max_range = max_range
|
|
proj.drop_gravity = projectile_gravity
|
|
proj.owner_player = player
|
|
light.light_energy = 2.0
|
|
light.omni_range = 4.0
|
|
proj.impact_type = "plasma"
|
|
|
|
# Spawn in the world
|
|
get_tree().current_scene.add_child(proj)
|