first commit
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
extends Node3D
|
||||
|
||||
var cW #current weapon
|
||||
var pointOfCollision : Vector3 = Vector3.ZERO
|
||||
var rng : RandomNumberGenerator
|
||||
|
||||
@onready var weaponManager : Node3D = %WeaponManager #weapon manager
|
||||
@onready var blood_particles_scene: PackedScene = preload("res://addons/NPC/enemy/blood_particles.tscn") # Укажи точный путь к файлу из Шага 2
|
||||
|
||||
func getCurrentWeapon(currWeap):
|
||||
#get current weapon resources
|
||||
cW = currWeap
|
||||
|
||||
func shoot():
|
||||
# БЛОКИРОВКА СТРЕЛЬБЫ ДЛЯ ДИАЛОГОВ И СМЕНЫ ОРУЖИЯ:
|
||||
# Если менеджеру оружия запрещено стрелять — принудительно выходим из функции
|
||||
var dialogue_ui = get_tree().current_scene.find_child("DialogueUI", true, false)
|
||||
if dialogue_ui:
|
||||
var dialogue_box = dialogue_ui.find_child("DialogueBox", true, false)
|
||||
# Если окно с текстом диалога СЕЙЧАС открыто и видимо на экране — выстрел ЗАПРЕЩЕН
|
||||
if dialogue_box and dialogue_box.visible:
|
||||
return # Мгновенно выходим, не давая проиграться коду ниже
|
||||
|
||||
|
||||
|
||||
|
||||
if !cW.isShooting and (
|
||||
#magazine isn't empty, and has >= ammo than the number of projectiles required for a shot
|
||||
(cW.totalAmmoInMag > 0 and cW.totalAmmoInMag >= cW.nbProjShotsAtSameTime)
|
||||
or
|
||||
#has all ammos in the magazine, and number of ammo is positive
|
||||
(cW.allAmmoInMag and weaponManager.ammoManager.ammoDict[cW.ammoType] > 0 and
|
||||
#has >= ammo than the number of projectiles required for a shot
|
||||
weaponManager.ammoManager.ammoDict[cW.ammoType] >= cW.nbProjShotsAtSameTime)
|
||||
) and !cW.isReloading:
|
||||
cW.isShooting = true
|
||||
|
||||
#number of successive shots (for example if 3, the weapon will shot 3 times in a row)
|
||||
for i in range(cW.nbProjShots):
|
||||
#same conditions has before, are checked before every shot
|
||||
if ((cW.totalAmmoInMag > 0 and cW.totalAmmoInMag >= cW.nbProjShotsAtSameTime)
|
||||
or (cW.allAmmoInMag and weaponManager.ammoManager.ammoDict[cW.ammoType] > 0) and
|
||||
weaponManager.ammoManager.ammoDict[cW.ammoType] >= cW.nbProjShotsAtSameTime):
|
||||
|
||||
weaponManager.weaponSoundManagement(cW.shootSound, cW.shootSoundSpeed)
|
||||
|
||||
if cW.shootAnimName != "":
|
||||
weaponManager.animManager.playAnimation("ShootAnim%s" % cW.weaponName, cW.shootAnimSpeed, true)
|
||||
else:
|
||||
print("%s doesn't have a shoot animation" % cW.weaponName)
|
||||
|
||||
#number projectiles shots at the same time (for example,
|
||||
#a shotgun shell is constituted of ~ 20 pellets that are spread across the target,
|
||||
#so 20 projectiles shots at the same time)
|
||||
for j in range(0, cW.nbProjShotsAtSameTime):
|
||||
if cW.allAmmoInMag: weaponManager.ammoManager.ammoDict[cW.ammoType] -= 1
|
||||
else: cW.totalAmmoInMag -= 1
|
||||
|
||||
#get the collision point
|
||||
pointOfCollision = getCameraPOV()
|
||||
|
||||
#call the fonction corresponding to the selected type
|
||||
if cW.type == cW.types.HITSCAN: hitscanShot(pointOfCollision)
|
||||
elif cW.type == cW.types.PROJECTILE: projectileShot(pointOfCollision)
|
||||
|
||||
if cW.showMuzzleFlash: weaponManager.displayMuzzleFlash()
|
||||
|
||||
weaponManager.cameraRecoilHolder.setRecoilValues(cW.baseRotSpeed, cW.targetRotSpeed)
|
||||
weaponManager.cameraRecoilHolder.addRecoil(cW.recoilVal)
|
||||
|
||||
await get_tree().create_timer(cW.timeBetweenShots).timeout
|
||||
|
||||
else:
|
||||
print("Not enought ammunitions to shoot")
|
||||
|
||||
cW.isShooting = false
|
||||
|
||||
func getCameraPOV():
|
||||
var camera : Camera3D = %Camera
|
||||
var window : Window = get_window()
|
||||
var viewport : Vector2i
|
||||
|
||||
#match viewport to window size, to ensure that the raycast goes in the right direction
|
||||
match window.content_scale_mode:
|
||||
window.CONTENT_SCALE_MODE_VIEWPORT:
|
||||
viewport = window.content_scale_size
|
||||
window.CONTENT_SCALE_MODE_CANVAS_ITEMS:
|
||||
viewport = window.content_scale_size
|
||||
window.CONTENT_SCALE_MODE_DISABLED:
|
||||
viewport = window.get_size()
|
||||
|
||||
#Start raycast in camera position, and launch it in camera direction
|
||||
var raycastStart = camera.project_ray_origin(viewport/2)
|
||||
var raycastEnd
|
||||
if cW.type == cW.types.HITSCAN: raycastEnd = raycastStart + camera.project_ray_normal(viewport/2) * cW.maxRange
|
||||
if cW.type == cW.types.PROJECTILE: raycastEnd = raycastStart + camera.project_ray_normal(viewport/2) * 280
|
||||
|
||||
#Create intersection space to contain possible collisions
|
||||
var newIntersection = PhysicsRayQueryParameters3D.create(raycastStart, raycastEnd)
|
||||
var intersection = get_world_3d().direct_space_state.intersect_ray(newIntersection)
|
||||
|
||||
#If the raycast has collide with something, return collision point transform properties
|
||||
if !intersection.is_empty():
|
||||
var collisionPoint = intersection.position
|
||||
return collisionPoint
|
||||
#Else, return the end of the raycast (so nothing, because he hasn't collide with anything)
|
||||
else:
|
||||
return raycastEnd
|
||||
|
||||
|
||||
|
||||
|
||||
func hitscanShot(pointOfCollisionHitscan : Vector3):
|
||||
rng = RandomNumberGenerator.new()
|
||||
|
||||
#set up weapon shot sprad
|
||||
var spread = Vector3(rng.randf_range(cW.minSpread, cW.maxSpread), rng.randf_range(cW.minSpread, cW.maxSpread), rng.randf_range(cW.minSpread, cW.maxSpread))
|
||||
|
||||
#calculate direction of the hitscan bullet
|
||||
var hitscanBulletDirection = (pointOfCollisionHitscan - cW.weaponSlot.attackPoint.get_global_transform().origin).normalized()
|
||||
|
||||
#create new intersection space to contain possibe collisions
|
||||
var newIntersection = PhysicsRayQueryParameters3D.create(cW.weaponSlot.attackPoint.get_global_transform().origin, pointOfCollisionHitscan + spread + hitscanBulletDirection * 2)
|
||||
newIntersection.collide_with_areas = true
|
||||
newIntersection.collide_with_bodies = true
|
||||
var hitscanBulletCollision = get_world_3d().direct_space_state.intersect_ray(newIntersection)
|
||||
|
||||
#if the raycast has collide
|
||||
if hitscanBulletCollision:
|
||||
var collider = hitscanBulletCollision.collider
|
||||
var colliderPoint = hitscanBulletCollision.position
|
||||
var colliderNormal = hitscanBulletCollision.normal
|
||||
var finalDamage : int
|
||||
|
||||
# --- НАНЕСЕНИЕ УРОНА ВРАГАМ ---
|
||||
if collider.is_in_group("Enemies"):
|
||||
finalDamage = cW.damagePerProj * cW.damageDropoff.sample(pointOfCollisionHitscan.distance_to(global_position) / cW.maxRange)
|
||||
|
||||
# Ищем HealthComponent внутри врага
|
||||
var enemy_health = collider.find_child("HealthComponent", true, false)
|
||||
if enemy_health:
|
||||
enemy_health.take_damage(finalDamage)
|
||||
|
||||
# --- СПАВН КРАСНЫХ КВАДРАТИКОВ (ИМПАКТ) ---
|
||||
# Переменная blood_particles_scene должна быть объявлена в самом верху скрипта ShootManager
|
||||
if has_node("blood_particles_scene") or (typeof(blood_particles_scene) if "blood_particles_scene" in self else null):
|
||||
var particles_instance = blood_particles_scene.instantiate()
|
||||
get_tree().get_root().add_child(particles_instance)
|
||||
particles_instance.global_position = colliderPoint # Ставим в точку попадания
|
||||
|
||||
if collider.has_method("hitscanHit"):
|
||||
collider.hitscanHit(finalDamage, hitscanBulletDirection, hitscanBulletCollision.position)
|
||||
|
||||
elif collider.is_in_group("EnemiesHead"):
|
||||
finalDamage = cW.damagePerProj * cW.headshotDamageMult * cW.damageDropoff.sample(pointOfCollisionHitscan.distance_to(global_position) / cW.maxRange)
|
||||
|
||||
var enemy_health = collider.find_child("HealthComponent", true, false)
|
||||
if enemy_health:
|
||||
enemy_health.take_damage(finalDamage)
|
||||
|
||||
# --- СПАВН КРАСНЫХ КВАДРАТИКОВ (ИМПАКТ В ГОЛОВУ) ---
|
||||
if "blood_particles_scene" in self and blood_particles_scene != null:
|
||||
var particles_instance = blood_particles_scene.instantiate()
|
||||
get_tree().get_root().add_child(particles_instance)
|
||||
particles_instance.global_position = colliderPoint
|
||||
|
||||
if collider.has_method("hitscanHit"):
|
||||
collider.hitscanHit(finalDamage, hitscanBulletDirection, hitscanBulletCollision.position)
|
||||
|
||||
elif collider.is_in_group("HitableObjects"):
|
||||
finalDamage = cW.damagePerProj * cW.damageDropoff.sample(pointOfCollisionHitscan.distance_to(global_position) / cW.maxRange)
|
||||
|
||||
var enemy_health = collider.find_child("HealthComponent", true, false)
|
||||
if enemy_health:
|
||||
enemy_health.take_damage(finalDamage / 6.0)
|
||||
|
||||
if collider.has_method("hitscanHit"):
|
||||
collider.hitscanHit(finalDamage/6.0, hitscanBulletDirection, hitscanBulletCollision.position)
|
||||
weaponManager.displayBulletHole(colliderPoint, colliderNormal)
|
||||
|
||||
else:
|
||||
weaponManager.displayBulletHole(colliderPoint, colliderNormal)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func projectileShot(pointOfCollisionProjectile : Vector3):
|
||||
rng = RandomNumberGenerator.new()
|
||||
|
||||
#set up weapon shot sprad
|
||||
var spread = Vector3(rng.randf_range(cW.minSpread, cW.maxSpread), rng.randf_range(cW.minSpread, cW.maxSpread), rng.randf_range(cW.minSpread, cW.maxSpread))
|
||||
|
||||
#Calculate direction of the projectile
|
||||
var projectileDirection = ((pointOfCollisionProjectile - cW.weaponSlot.attackPoint.get_global_transform().origin).normalized() + spread)
|
||||
|
||||
#Instantiate projectile
|
||||
var projInstance = cW.projRef.instantiate()
|
||||
|
||||
#set projectile properties
|
||||
projInstance.global_transform = cW.weaponSlot.attackPoint.global_transform
|
||||
projInstance.direction = projectileDirection
|
||||
projInstance.damage = cW.damagePerProj
|
||||
projInstance.timeBeforeVanish = cW.projTimeBeforeVanish
|
||||
projInstance.gravity_scale = cW.projGravityVal
|
||||
projInstance.isExplosive = cW.isProjExplosive
|
||||
|
||||
get_tree().get_root().add_child(projInstance)
|
||||
|
||||
projInstance.set_linear_velocity(projectileDirection * cW.projMoveSpeed)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user