signalis/src/entity/equipped_weapon.ts

102 lines
3.1 KiB
TypeScript

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 '../extension/annotations'
import Weapon from '../item/weapon'
import AsyncResourceHandler from '../global/async_resource_handler'
import { forward } from '../extension/vec'
import DebugDraw from '../global/debug_draw'
import { find_in_ancestors, find_in_descendents, has_method } from '../extension/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<Weapon>
@signal()
unequipped!: Signal1<Weapon>
@onready('FireRate')
_fire_rate_timer!: Timer
has_equipped_weapon(): boolean {
return this._has_equipped_weapon
}
_ready(): void {
if (this.starting_weapon != null) {
AsyncResourceHandler.load<Weapon>(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<Node> = 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) {
AsyncResourceHandler.instance.load<PackedScene>(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_ancestors(result.get('collider'), n => n instanceof PhysicsBody3D)
const health = find_in_descendents<Node & Damageable>(root, n => has_method('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
}
}
}