Files
tactical-shooter/client/template/scripts/Projectiles/Projectile.gd
T
shawn ad48f38ca5 feat: integrate ChaffGames FPS template as local player controller
Replaces the overhead box-mesh view with a full FPS character:

- mouse look, WASD movement, jump (Space), crouch (C), sprint (Shift), lean (Q/E)

- 6 weapons (LMB shoot, R reload, scroll switch, F melee, G drop)

- Crosshair HUD, ammo counter, sprint bar

- Client sends position to server at 20Hz for multiplayer visibility

- Server broadcasts positions to all peers

- Remote players shown as boxes (placeholder)

source: https://godotengine.org/asset-library/asset/2652 (MIT license)
2026-07-02 00:26:45 -04:00

138 lines
5.0 KiB
GDScript

extends Node3D
class_name Projectile
signal Hit_Successfull
## Can Be Either A Hit Scan or Rigid Body Projectile. If Rigid body is select a Rigid body must be provided.
@export_enum ("Hitscan","Rigidbody_Projectile","over_ride") var Projectile_Type: String = "Hitscan"
@export var Display_Debug_Decal: bool = true
@export_category("Rigid Body Projectile Properties")
@export var Projectile_Velocity: int
@export var Expirey_Time: int = 10
@export var Rigid_Body_Projectile: PackedScene
@export var pass_through: bool = false
@onready var Debug_Bullet = preload("res://client/template/Spawnable_Objects/hit_debug.tscn")
var damage: float = 0
var Projectiles_Spawned = []
var hit_objects: Array = []
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
get_tree().create_timer(Expirey_Time).timeout.connect(_on_timer_timeout)
func _Set_Projectile(_damage: int = 0,_spread:Vector2 = Vector2.ZERO, _Range: int = 1000, origin_point: Vector3 = Vector3.ZERO):
damage = _damage
Fire_Projectile(_spread,_Range,Rigid_Body_Projectile, origin_point)
func Fire_Projectile(_spread: Vector2 ,_range: int, _proj:PackedScene, origin_point: Vector3):
var Camera_Collision = Camera_Ray_Cast(_spread,_range)
match Projectile_Type:
"Hitscan":
Hit_Scan_Collision(Camera_Collision, damage,origin_point)
"Rigidbody_Projectile":
Launch_Rigid_Body_Projectile(Camera_Collision, _proj,origin_point)
"over_ride":
_over_ride_collision(Camera_Collision, damage)
func _over_ride_collision(_camera_collision:Array, _damage: float) -> void:
pass
func Camera_Ray_Cast(_spread: Vector2 = Vector2.ZERO, _range: float = 1000):
var _Camera = get_viewport().get_camera_3d()
var _Viewport = get_viewport().get_size()
var Ray_Origin = _Camera.project_ray_origin(_Viewport/2)
var Ray_End = (Ray_Origin + _Camera.project_ray_normal((_Viewport/2)+Vector2i(_spread)) *_range)
var New_Intersection:PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(Ray_Origin,Ray_End)
New_Intersection.set_collision_mask(0b11101101)
New_Intersection.set_hit_from_inside(false) # In Jolt this is set to true by defualt
var Intersection = get_world_3d().direct_space_state.intersect_ray(New_Intersection)
if not Intersection.is_empty():
var Collision = [Intersection.collider,Intersection.position,Intersection.normal]
return Collision
else:
return [null,Ray_End,null]
func Hit_Scan_Collision(Collision: Array,_damage: float, origin_point: Vector3):
var Point = Collision[1]
if Collision[0]:
Load_Decal(Point, Collision[2])
if Collision[0].is_in_group("Target"):
var Bullet = get_world_3d().direct_space_state
var Bullet_Direction = (Point - origin_point).normalized()
var New_Intersection = PhysicsRayQueryParameters3D.create(origin_point,Point+Bullet_Direction*2)
New_Intersection.set_collision_mask(0b11101101)
New_Intersection.set_hit_from_inside(false)
New_Intersection.set_exclude(hit_objects)
var Bullet_Collision = Bullet.intersect_ray(New_Intersection)
if Bullet_Collision:
Hit_Scan_damage(Bullet_Collision.collider, Bullet_Direction,Bullet_Collision.position,_damage)
if pass_through and check_pass_through(Bullet_Collision.collider, Bullet_Collision.rid):
var pass_through_collision : Array = [Bullet_Collision.collider, Bullet_Collision.position, Bullet_Collision.normal]
var pass_through_damage: float = damage/2
Hit_Scan_Collision(pass_through_collision,pass_through_damage,Bullet_Collision.position)
return
queue_free()
func check_pass_through(collider: Node3D, rid: RID)-> bool:
var valid_pass_though: bool = false
if collider.is_in_group("Pass Through"):
hit_objects.append(rid)
valid_pass_though = true
return valid_pass_though
func Hit_Scan_damage(Collider, Direction, Position, _damage):
if Collider.is_in_group("Target") and Collider.has_method("Hit_Successful"):
Hit_Successfull.emit()
Collider.Hit_Successful(_damage, Direction, Position)
func Load_Decal(_pos,_normal):
if Display_Debug_Decal:
var rd = Debug_Bullet.instantiate()
var world = get_tree().get_root()
world.add_child(rd)
rd.global_translate(_pos+(_normal*.01))
func Launch_Rigid_Body_Projectile(Collision_Data, _projectile, _origin_point):
var _Point = Collision_Data[1]
var _Norm = Collision_Data[2]
var _proj : RigidBody3D = _projectile.instantiate()
_proj.position = _origin_point
var world = get_tree().get_first_node_in_group("World")
world.add_child(_proj)
_proj.look_at(_Point)
Projectiles_Spawned.push_back(_proj)
_proj.body_entered.connect(_on_body_entered.bind(_proj,_Norm))
var _Direction = (_Point - _origin_point).normalized()
_proj.set_linear_velocity(_Direction*Projectile_Velocity)
func _on_body_entered(body, _proj, _norm):
if body.is_in_group("Target") && body.has_method("Hit_Successful"):
body.Hit_Successful(damage)
Hit_Successfull.emit()
Load_Decal(_proj.get_position(),_norm)
_proj.queue_free()
Projectiles_Spawned.erase(_proj)
if Projectiles_Spawned.is_empty():
queue_free()
func _on_timer_timeout():
queue_free()