class_name WeaponBase extends Node2D enum WeaponTag { CAN_RETURN, CAN_BLEED, CAN_CHAIN, CAN_FORK, } @export var attack_cd: float @export var attack_damage: float @export var attack_aoe: float @export var attack_duration: float @export var attack_range: float @export var attack_crit_chance: float = 0.05 @export var return_chance: float = 0.0 @export var bleed_chance: float = 0.0 @export var bleed_duration: float = 5.0 @export var chain_chance: float = 0.0 @export var tags: Array[String] = [] @export var modifiers: Array[WeaponModBase] = [] @onready var active_cd_timer: Timer = $ActiveCDTimer var _player: Player func _ready() -> void: _player = get_tree().get_first_node_in_group(GlobalConst.GROUP_PLAYER) func _on_attack_cd_timer_timeout() -> void: do_attack() func do_attack() -> void: push_error("%s does not implement do_attack" % self) func do_active() -> void: if not active_cd_timer.is_stopped(): return active_cd_timer.start() _do_active() func add_mod(mod: WeaponModBase) -> void: modifiers.append(mod) func _do_active() -> void: push_error("%s does not implement do_active" % self) func find_target_in_radius() -> EnemyBase: var space_state: PhysicsDirectSpaceState2D = get_world_2d().direct_space_state var shape := CircleShape2D.new() shape.radius = attack_range var query := PhysicsShapeQueryParameters2D.new() query.shape = shape query.transform = Transform2D(0, global_position) query.collision_mask = 2 query.collide_with_bodies = true var results := space_state.intersect_shape(query) if len(results) < 1: return null var closest: PhysicsBody2D = results[0]["collider"] for r in results: var c: PhysicsBody2D = r["collider"] if not c.is_in_group(GlobalConst.GROUP_ENEMY): continue if ( c.global_position.distance_to(global_position) < closest.global_position.distance_to(global_position) ): closest = c return closest func get_calculated(key: String) -> Variant: return WeaponModBase.get_calculated(self, key) func has_property(key: String) -> bool: for prop in get_property_list(): if prop.name == key: return true return false func did_crit() -> bool: var weapon_crit = get_calculated("attack_crit_chance") var player_crit = _player.player_stats.get_final("crit_chance", _player.modifiers) return randf() >= 1 - weapon_crit + player_crit func did_bleed() -> bool: return randf() >= 1 - bleed_chance func base_damage() -> Array[Variant]: var damage = get_calculated("attack_damage") var is_crit := did_crit() if is_crit: damage *= _player.player_stats.get_final("crit_multiplier", _player.modifiers) return [damage, is_crit]