Advertisement
Lakamfo

weapon_script

May 19th, 2024
1,277
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
GDScript 16.67 KB | Gaming | 0 0
  1. class_name Weapon extends Node3D
  2.  
  3. @export_category("Basic")
  4. ##Rate of fire per minute
  5. @export var fire_rate = 800
  6. ##Rate of burst mode fire per minute
  7. @export var burst_fire_rate = 800
  8. ##Default fire mode
  9. @export_enum("semi", "auto", "burst") var fire_mode : int = 1
  10. @export_flags("semi", "auto", "burst") var available_shooting_modes = 3
  11. ##Maximum engagement distance
  12. @export var fire_distance : float = 50
  13. ##Zoom X while aiming
  14. @export var aim_camera_zoom : float = 2
  15.  
  16. @export_group("Bullets")
  17. @export var clip_size : int = 30
  18. @export var magazine_size : int = 300
  19. ##Additional bullet in chamber when tactical reloaded
  20. @export var bullet_in_chamber : bool = true
  21. @export var burst_size : int = 3
  22.  
  23. @export_group("Damage")
  24. ##Base damage.
  25. ##Damage depends on the body part and is calculated using the formula:
  26. ##result_damage = damage * part_mult
  27. @export var damage : float = 27
  28.  
  29. @export var head_mult : float = 2
  30. @export var torso_mult : float = 1
  31. @export var limbs_mult : float = 0.7
  32.  
  33. @export_group("Recoil")
  34. @export var hip_camera_kick : Vector3
  35. @export var aim_camera_kick : Vector3
  36. #@export var max_camera_kick : Vector3
  37.  
  38. ##Does nothing
  39. @export var random_mult : float = 1
  40.  
  41. ##How fast camera snaps to kick
  42. @export var snappinnes = 6
  43. ##How fast camera returns to normal rotation
  44. @export var return_speed = 2
  45.  
  46. @export_subgroup("NonStop paremetres")
  47. ##If this option is activated, the camera's recoil accumulates
  48. @export var non_stop_mult_enabled : bool = true
  49. @export var max_non_stop_mult : float = 5
  50. @export var min_non_stop_mult : float = 1
  51. ##How much accumulates per shoot
  52. @export var non_stop_increase : float = 0.05
  53. ##Waiting for the last shot to reset the recoil
  54. @export var non_stop_reset_threshold : float = 0.5
  55.  
  56.  
  57. @export_group("Position & Procedural animation")
  58. ##Procedural animations for aiming
  59. @export var procedural_animation_enabled : bool = true
  60.  
  61. @export_subgroup("Hip")
  62. @export var standart_position : Vector3
  63. @export var standart_rotation : Vector3
  64. @export_subgroup("Aim")
  65. @export var aim_position : Vector3
  66. @export var aim_rotation : Vector3
  67. ##How long does the animation last in seconds
  68. @export var aim_speed : float = 1.0
  69.  
  70. @export_group("Shell")
  71. @export_enum("9mm:0","7mm:1","5mm:2") var shell_type : int = 0
  72. @export var min_impulse : Vector3
  73. @export var max_impulse : Vector3
  74. @export_subgroup("Custom shell model")
  75. @export var custom_model : PackedScene
  76.  
  77. @export_group("Sounds & Animations")
  78. @export_placeholder("You can write several animations separated by commas") var idle_animation : String = ""
  79. @export_placeholder("You can write several animations separated by commas") var take_out_animation : String = ""
  80. @export_subgroup("Fire Animations")
  81. @export_placeholder("You can write several animations separated by commas") var fire_animation : String = ""
  82. @export_placeholder("You can write several animations separated by commas") var aim_fire_animation : String = ""
  83. #@export_placeholder("You can write several animations separated by commas") var aim_reload_animation : String = ""
  84. @export_subgroup("Reload Animations")
  85. @export_placeholder("You can write several animations separated by commas") var tactical_reload_animation : String = ""
  86. @export_placeholder("You can write several animations separated by commas") var reload_animation : String = ""
  87.  
  88.  
  89. @export_group("UI")
  90. @export var icon : CompressedTexture2D = preload("res://icon.svg")
  91. @export var hit_marker_standart_color : Color = Color(1, 1, 1)
  92. @export var hit_marker_critical_color : Color = Color(0.56878209114075, 0.16045689582825, 0.2052811384201)
  93.  
  94. @export_group("Nodes")
  95. ## Weapon`s animation node
  96. @export_node_path("AnimationPlayer") var animation_player
  97. ## Weapon`s shot sound
  98. @export_node_path("AudioStreamPlayer3D") var audio_stream_player
  99. ## Weapon`s change mode click
  100. @export_node_path("AudioStreamPlayer3D") var audio_fire_mode_switch_stream_player
  101. ## Weapon`s flash
  102. @export_node_path("OmniLight3D") var omni_light
  103. ## Weapon`s shells spawn position
  104. @export_node_path("Marker3D") var shell_position
  105. ## Weapon`s sparkles and smoke
  106. @export_node_path("GPUParticles3D") var gpu_particles
  107.  
  108. @onready var bullet_pos = $bullet_tracer_pos
  109. var scene_root
  110.  
  111. #NODES
  112. var _animation_player : AnimationPlayer
  113. var _audio_stream_player : AudioStreamPlayer3D
  114. var _omni_light : OmniLight3D
  115. var _gpu_particles : GPUParticles3D
  116. var _shell_position : Marker3D
  117. var _audio_fire_mode_switch_stream_player : AudioStreamPlayer3D
  118.  
  119. var player
  120. var timer : Timer = Timer.new()
  121. var non_stop_timer : Timer = Timer.new()
  122.  
  123. #Shells
  124. var shell_9mm : PackedScene = preload("res://player/weapons/shells/9_mm_shell.tscn")
  125. var shell_7mm : PackedScene
  126. var shell_5mm : PackedScene
  127.  
  128. var bullet_hole_decal : PackedScene = preload("res://player/weapons/bullet_hole.tscn")
  129.  
  130. var fire_mode_str : Array = ["SEMI","AUTO","BURST"]
  131.  
  132. #Internal variables
  133. var is_aiming : bool = false
  134. var previous_aim_state : bool = is_aiming
  135.  
  136. var timer_wait : float = 60.0 / fire_rate
  137. var non_stop : float = 1.0
  138. var weapon_ray_cast_3d : RayCast3D
  139. var ui_element
  140.  
  141. var is_reloading : bool = false
  142. var is_take_out : bool = false
  143.  
  144. var clip : int
  145. var magazine : int = magazine_size
  146.  
  147. var _animations_fire : AnimationsPack = AnimationsPack.new()
  148. var _animations_aim_fire : AnimationsPack = AnimationsPack.new()
  149. var _animations_idle : AnimationsPack = AnimationsPack.new()
  150. var _animations_take_out : AnimationsPack = AnimationsPack.new()
  151. var _animations_tactical_reload : AnimationsPack = AnimationsPack.new()
  152. var _animations_reload : AnimationsPack = AnimationsPack.new()
  153.  
  154. class AnimationsPack:
  155.     var _animations_pack : PackedStringArray
  156.     var _pack_size : int = -1
  157.    
  158.     func parse(data : String, splitter : String = ",") -> void:
  159.         _animations_pack = data.split(splitter, false)
  160.        
  161.         _pack_size = _animations_pack.size() - 1
  162.    
  163.     func get_size() -> int:
  164.         return _pack_size
  165.    
  166.     func get_pack() -> PackedStringArray:
  167.         return _animations_pack
  168.    
  169.     func get_rand_animation() -> String:
  170.         if _pack_size > -1:
  171.             return _animations_pack[randi_range(0,_pack_size)]
  172.         else:
  173.             return "null"
  174.  
  175.  
  176. var bullet_tracer = preload("res://player/weapons/in_game/bullet_tracer.tscn")
  177. var tracer_instance
  178. var shell_instance
  179.  
  180. #Signals
  181. #Weapon_recoil in Event Bus
  182. #Weapon_aim in Event Bus
  183.  
  184. func _ready() -> void:
  185.     add_child(timer)
  186.     add_child(non_stop_timer)
  187.    
  188.     timer.one_shot = true; non_stop_timer.one_shot = true
  189.     timer.autostart = false; non_stop_timer.autostart = false
  190.    
  191.     non_stop = min_non_stop_mult
  192.    
  193.     if animation_player != null: _animation_player = get_node_or_null(animation_player)
  194.     if audio_stream_player != null: _audio_stream_player = get_node_or_null(audio_stream_player)
  195.     if audio_fire_mode_switch_stream_player != null: _audio_fire_mode_switch_stream_player = get_node_or_null(audio_fire_mode_switch_stream_player)
  196.     if omni_light != null: _omni_light = get_node_or_null(omni_light)
  197.     if gpu_particles != null: _gpu_particles = get_node_or_null(gpu_particles)
  198.     if shell_position != null: _shell_position = get_node_or_null(shell_position)
  199.    
  200.     _animations_fire.parse(fire_animation)
  201.     _animations_aim_fire.parse(aim_fire_animation)
  202.     _animations_idle.parse(idle_animation)
  203.     _animations_reload.parse(reload_animation)
  204.     _animations_tactical_reload.parse(tactical_reload_animation)
  205.     _animations_take_out.parse(take_out_animation)
  206.    
  207.     _play_animation(_animations_fire.get_rand_animation())
  208.    
  209.     scene_root = get_tree().root
  210.    
  211.     magazine = magazine_size
  212.     clip = clip_size
  213.    
  214.     if _animation_player:
  215.         _animation_player.animation_finished.connect(_animation_handler)
  216.    
  217.     _change_shooting_mode(true)
  218.     _update_ui()
  219.  
  220. func _check(_delta : float):
  221.     if !visible:
  222.         return
  223.    
  224.     is_aiming = Input.is_action_pressed("mouse_2")
  225.    
  226.     if non_stop_timer.is_stopped():
  227.         non_stop = min_non_stop_mult
  228.    
  229.     if !timer.is_stopped() or is_reloading or is_take_out:
  230.         return
  231.    
  232.     match fire_mode:
  233.         0: # Semi
  234.             if Input.is_action_just_pressed("mouse_1"):
  235.                 timer_wait = 60.0 / fire_rate
  236.                 _fire()
  237.         1: #Auto
  238.             if Input.is_action_pressed("mouse_1"):
  239.                 timer_wait = 60.0 / fire_rate
  240.                 _fire()
  241.         2: #Burst
  242.             if Input.is_action_just_pressed("mouse_1"):
  243.                 timer_wait = 60.0 / burst_fire_rate
  244.                 for bullet in burst_size:
  245.                     _fire()
  246.                     timer.start(timer_wait)
  247.                     await timer.timeout
  248.  
  249. func _physics_process(delta):
  250.     _check(delta)
  251.    
  252.     if Input.is_action_just_pressed("b"):
  253.         _change_shooting_mode(false)
  254.    
  255.     if Input.is_action_just_pressed("r"):
  256.         _reload()
  257.  
  258. func _process(delta: float) -> void:
  259.     _check(delta)
  260.    
  261.     _procedural_animation(delta)
  262.  
  263. func _fire():
  264.     if !clip > 0:
  265.         return
  266.    
  267.     _play_vfx()
  268.    
  269.     if custom_model and shell_position:
  270.         pass
  271.    
  272.     elif shell_position and Global.decals_enabled:
  273.         match shell_type:
  274.             0:
  275.                 var instance = shell_9mm.instantiate()
  276.                 #_shell_position.add_child(instance)
  277.                 scene_root.add_child(instance)
  278.                 instance.global_transform = _shell_position.global_transform
  279.                
  280.                 instance.top_level = true
  281.                 instance.apply_central_impulse(global_transform.basis * _get_random_impulse())
  282.                 instance.apply_torque_impulse(_get_random_impulse())
  283.                
  284.                 instance = null
  285.             1:
  286.                 pass
  287.             2:
  288.                 pass
  289.    
  290.     clip -= 1
  291.     _hit_reg()
  292.    
  293.     _update_ui()
  294.    
  295.     random_recoil()
  296.     timer.start(timer_wait)
  297.     non_stop_timer.start(non_stop_reset_threshold)
  298.    
  299.     if _animation_player != null and fire_animation != "":
  300.         _animation_player.stop()
  301.        
  302.         if(aim_fire_animation != "") and is_aiming:
  303.             _play_animation(_animations_aim_fire.get_rand_animation())
  304.         else:
  305.             _play_animation(_animations_fire.get_rand_animation())
  306.    
  307.     if _audio_stream_player != null:
  308.         _audio_stream_player.pitch_scale = randf_range(0.9,1.1)
  309.         _audio_stream_player.play()
  310.    
  311.     non_stop += non_stop_increase
  312.     non_stop = clamp(non_stop, min_non_stop_mult, max_non_stop_mult)
  313.  
  314. func _hit_reg():
  315.     if weapon_ray_cast_3d:
  316.         var collider = weapon_ray_cast_3d.get_collider()
  317.         if collider and collider.has_method("get_hit"):
  318.             var final_damage = damage
  319.             if collider is Hitbox:
  320.                 match collider.type:
  321.                     0:
  322.                         final_damage *= head_mult
  323.                         EventBus.emit_signal("weapon_hitted", hit_marker_critical_color)
  324.                     1:
  325.                         final_damage *= torso_mult
  326.                         EventBus.emit_signal("weapon_hitted", hit_marker_standart_color)
  327.                     2:
  328.                         final_damage *= limbs_mult
  329.                         EventBus.emit_signal("weapon_hitted", hit_marker_standart_color)
  330.            
  331.             collider.call("get_hit", final_damage, Vector3())
  332.  
  333.  
  334. func _reload():
  335.     #While shooting, the player cannot start reloading
  336.     if (_animation_player.current_animation in _animations_fire.get_pack() or _animation_player.current_animation in _animations_reload.get_pack()):
  337.         return
  338.    
  339.     if (is_reloading):
  340.         return
  341.    
  342.    
  343.     var wasted_ammo_size : int = clip_size - (clip - int(bullet_in_chamber and clip > 0))
  344.    
  345.     if wasted_ammo_size <= 0 || magazine <= 0:
  346.         return
  347.    
  348.     if (wasted_ammo_size <= magazine):
  349.         magazine -= wasted_ammo_size
  350.        
  351.         clip += wasted_ammo_size
  352.     else:
  353.         clip += magazine
  354.         magazine = 0
  355.    
  356.     if clip > 0 and _animations_tactical_reload.get_size() > -1:
  357.         _play_animation(_animations_tactical_reload.get_rand_animation())
  358.         is_reloading = true
  359.     elif _animations_reload.get_size() > -1:
  360.         _play_animation(_animations_reload.get_rand_animation())
  361.         is_reloading = true
  362.    
  363.    
  364.     EventBus.emit_signal("weapon_reload", is_reloading)
  365.    
  366.     _update_ui()
  367.  
  368. func _change_shooting_mode(ready_update : bool = false):
  369.     # fire_mode = semi : 0, auto : 1, burst : 2
  370.     # Single modes are not described, because they don't need logic
  371.     if(ready_update):
  372.         match available_shooting_modes:
  373.             1: #Semi
  374.                 fire_mode = 0
  375.             2: # Auto
  376.                 fire_mode = 1
  377.             4: # Burst
  378.                 fire_mode = 2
  379.     else:
  380.         match available_shooting_modes:
  381.             1: #Semi
  382.                 fire_mode = 0
  383.             2: # Auto
  384.                 fire_mode = 1
  385.             4: # Burst
  386.                 fire_mode = 2
  387.             3: #Semi : 1 + Auto : 2
  388.                 if(fire_mode == 0): fire_mode = 1
  389.                 else : fire_mode = 0
  390.                 print("shkibididop")
  391.             5: #Burst : 4 + Semi : 1
  392.                 if(fire_mode == 0): fire_mode = 2
  393.                 else : fire_mode = 0
  394.             6: #Auto : 2 + burst : 4
  395.                 if(fire_mode == 1): fire_mode = 2
  396.                 else : fire_mode = 1
  397.             7: #Semi : 1 + Auto : 2 + Burst : 4
  398.                 if(fire_mode == 0): fire_mode = 1
  399.                 elif(fire_mode == 1) : fire_mode = 2
  400.                 else : fire_mode =  0
  401.        
  402.         if _audio_fire_mode_switch_stream_player:
  403.             _audio_fire_mode_switch_stream_player.pitch_scale = randf_range(0.9,1.1)
  404.             _audio_fire_mode_switch_stream_player.play()
  405.    
  406.     if(available_shooting_modes == 0):
  407.         DebugOutput.print_warning("Please select at least one shooting mode! : " + str(self))
  408.    
  409.     _update_ui()
  410.  
  411. func random_recoil():
  412.     var camera_kick : Vector3
  413.    
  414.     if is_aiming:
  415.         camera_kick = Vector3(
  416.             randf_range(aim_camera_kick.x / 2,aim_camera_kick.x),
  417.             randf_range(-aim_camera_kick.y,aim_camera_kick.y),
  418.             randf_range(-aim_camera_kick.z,aim_camera_kick.z)
  419.         ) * non_stop
  420.     else:
  421.         camera_kick = Vector3(
  422.             randf_range( hip_camera_kick.x / 2,hip_camera_kick.x),
  423.             randf_range(-hip_camera_kick.y,hip_camera_kick.y),
  424.             randf_range(-hip_camera_kick.z,hip_camera_kick.z)
  425.         ) * non_stop
  426.    
  427.     EventBus.emit_signal("weapon_recoil", camera_kick, snappinnes, return_speed)
  428.  
  429. func _update_ui():
  430.     if ui_element != null:
  431.         ui_element.ammos.text = str(clip) + "/" + str(magazine)
  432.         ui_element.fire_mode.text = fire_mode_str[fire_mode]
  433.  
  434. func _get_random_impulse():
  435.     return Vector3(
  436.                     randf_range(min_impulse.x,max_impulse.x),
  437.                     randf_range(min_impulse.y,max_impulse.y),
  438.                     randf_range(min_impulse.z,max_impulse.z)
  439.                     )
  440.  
  441. func _procedural_animation(_delta : float):
  442.     if !procedural_animation_enabled:
  443.         return
  444.     if is_aiming == previous_aim_state:
  445.         return
  446.    
  447.     previous_aim_state = is_aiming
  448.     var new_tween = get_tree().create_tween()
  449.    
  450.     if (is_aiming):
  451.         EventBus.emit_signal("weapon_aim", aim_camera_zoom, aim_speed, is_aiming)
  452.     else:
  453.         EventBus.emit_signal("weapon_aim", 1, aim_speed, is_aiming)
  454.    
  455.     if is_aiming:
  456.         new_tween.tween_property(self, "position",aim_position, aim_speed).set_trans(Tween.TRANS_SINE)
  457.         new_tween.parallel()
  458.         new_tween.tween_property(self, "rotation_degrees",aim_rotation, aim_speed).set_trans(Tween.TRANS_SINE)
  459.        
  460.         new_tween.play()
  461.         #position = lerp(position, aim_position, aim_speed * delta)
  462.         #rotation_degrees = lerp(rotation_degrees, aim_rotation, aim_speed * delta)
  463.     else:
  464.         new_tween.tween_property(self, "position",standart_position, aim_speed).set_trans(Tween.TRANS_SINE)
  465.         new_tween.parallel()
  466.         new_tween.tween_property(self, "rotation_degrees",standart_rotation, aim_speed).set_trans(Tween.TRANS_SINE)
  467.        
  468.         new_tween.play()
  469.        
  470.         #position = lerp(position, standart_position, aim_speed * delta)
  471.         #rotation_degrees = lerp(rotation_degrees, standart_rotation, aim_speed * delta)
  472.  
  473. func _animation_handler(animation_name : String):
  474.     if (animation_name in _animations_take_out.get_pack() + _animations_fire.get_pack() + _animations_reload.get_pack() + _animations_tactical_reload.get_pack()):
  475.         var rand_animation : String = _animations_idle.get_rand_animation()
  476.        
  477.         if(animation_name in _animations_reload.get_pack() + _animations_tactical_reload.get_pack()):
  478.             is_reloading = false
  479.             EventBus.emit_signal("weapon_reload", is_reloading)
  480.        
  481.         if(animation_name in _animations_take_out.get_pack()):
  482.             is_take_out = false
  483.        
  484.         _play_animation(rand_animation)
  485.  
  486. func _play_vfx():
  487.     if !Global.decals_enabled:
  488.         return
  489.    
  490.     tracer_instance = bullet_tracer.instantiate()
  491.     bullet_pos.add_child(tracer_instance)
  492.     #bullet_tracer.global_transform = bullet_pos.global_transform
  493.     tracer_instance.top_level = true
  494.    
  495.     if weapon_ray_cast_3d.is_colliding():
  496.         var collider = weapon_ray_cast_3d.get_collider()
  497.         if collider:
  498.             if !collider.is_in_group("dynamic_object"):
  499.                 var decal = bullet_hole_decal.instantiate()
  500.                 decal.collider = collider
  501.                 collider.add_child(decal)
  502.                 decal.global_transform.origin = weapon_ray_cast_3d.get_collision_point()
  503.                 decal.look_at(Global.player.neck.global_rotation, weapon_ray_cast_3d.get_collision_normal())
  504.    
  505.     if _gpu_particles:_gpu_particles.emitting = true
  506.     if _omni_light:_omni_light.visible = true
  507.     await get_tree().create_timer(timer_wait).timeout
  508.     if _gpu_particles:_gpu_particles.emitting = false
  509.     if _omni_light:_omni_light.visible = false
  510.  
  511.  
  512. func _play_animation(anim_name : String):
  513.     if _animation_player:
  514.         _animation_player.play(anim_name)
  515.  
  516. func change_visible(visible_weapon : bool = false):
  517.     if (visible_weapon == visible):
  518.         return
  519.     else:
  520.         visible = visible_weapon
  521.    
  522.         if(visible_weapon and _animations_take_out.get_size() > -1):
  523.             _animation_player.stop()
  524.             _play_animation(_animations_take_out.get_rand_animation())
  525.             is_take_out = true
  526.  
  527. #Func for track in AnimationPlayer
  528. func end_reloading():
  529.     is_reloading = false
  530.     EventBus.emit_signal("weapon_reload", is_reloading)
  531.  
  532. func end_take_out():
  533.     is_take_out = false
Tags: Godot
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement