import { Color, GArray, Node, Node3D, PackedScene, PhysicsBody3D, PhysicsRayQueryParameters3D, Signal1, Timer, Vector3 } from 'godot' import { export_file, signal } from 'godot.annotations' import { export_node, onready } from './annotations' import Weapon from './weapon' import AsyncResourceLoader from './async_resource_loader' import { forward } from './vec' import DebugDraw from './debug_draw' import { find_in_anscestors, find_in_descendents, implements_interface } from './node' import { Damageable } from './health' const { MULTIPLY: mul, ADD: add } = Vector3 export default class EquippedWeapon extends Node3D { @export_file('*.tres') starting_weapon?: string _starting_weapon?: Weapon @export_node() transform_parent!: Node3D _equipped_weapon?: Weapon _has_equipped_weapon: boolean = false @signal() equipped!: Signal1 @signal() unequipped!: Signal1 @onready('FireRate') _fire_rate_timer!: Timer has_equipped_weapon(): boolean { return this._has_equipped_weapon } _ready(): void { if (this.starting_weapon != null) { AsyncResourceLoader.instance.load(this.starting_weapon, 'Weapon').then(weapon => this.equip(weapon)) } } unequip() { if (this._equipped_weapon != null) { const previous = this._equipped_weapon this._equipped_weapon = undefined this._has_equipped_weapon = false this.unequipped.emit(previous) } } _parent_scene_to_transform(scene: PackedScene) { const children: GArray = this.transform_parent.get_children() for (const child of children) { child.queue_free() } const node = scene.instantiate() as Node3D this.add_child(node) node.reparent(this.transform_parent, false) } equip(weapon: Weapon) { AsyncResourceLoader.instance.load(weapon.scene, 'PackedScene') .then(scene => this._parent_scene_to_transform(scene)) if (this._has_equipped_weapon) { this.unequip() } this._has_equipped_weapon = true this._equipped_weapon = weapon this._fire_rate_timer.wait_time = weapon.fire_rate this.equipped.emit(weapon) } fire(): boolean { if (this._equipped_weapon && this._fire_rate_timer.time_left <= 0) { this._fire_rate_timer.start() const space_state = this.get_world_3d().direct_space_state const origin = this.global_position const end = add(origin, mul(forward(this.global_transform), this._equipped_weapon.range)) const query = PhysicsRayQueryParameters3D.create(origin, end) const result = space_state.intersect_ray(query) if (!result.is_empty()) { DebugDraw.draw_ray(query, Color.DARK_RED) const root = find_in_anscestors(result.get('collider'), n => n instanceof PhysicsBody3D) const health = find_in_descendents(root, n => implements_interface(['apply_damage'], n)) if (health != null) { health.apply_damage(this, this._equipped_weapon.damage) } } else { DebugDraw.draw_ray(query, Color.DARK_GREEN) } return true } else { return false } } }