game: add weapon base and sword #13
| @@ -18,6 +18,7 @@ const GROUP_DAMAGEABLE = "damagable" | |||||||
| const GROUP_PLAYER = "player" | const GROUP_PLAYER = "player" | ||||||
| const GROUP_XP_ORB = "xp_orb" | const GROUP_XP_ORB = "xp_orb" | ||||||
| const GROUP_PICKUP = "pickup" | const GROUP_PICKUP = "pickup" | ||||||
|  | const GROUP_PROJ_MANAGER = "proj_manager" | ||||||
|  |  | ||||||
| enum ModRarity { LEGENDARY, EPIC, RARE, NORMAL } | enum ModRarity { LEGENDARY, EPIC, RARE, NORMAL } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ enemy="Group containing all enemies" | |||||||
| damagable="Can be damaged using take_damage" | damagable="Can be damaged using take_damage" | ||||||
| xp_orb="Experience orbs" | xp_orb="Experience orbs" | ||||||
| pickup="Items that can be picked up" | pickup="Items that can be picked up" | ||||||
|  | proj_manager="Parent node for all projectiles" | ||||||
|  |  | ||||||
| [input] | [input] | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,102 +0,0 @@ | |||||||
| extends Node2D |  | ||||||
|  |  | ||||||
| @export var default_attack_time: float = 0.5 |  | ||||||
| @export var player: Player = null |  | ||||||
|  |  | ||||||
| @onready var trigger_area: Area2D = $TriggerArea |  | ||||||
| @onready var trigger_collision: CollisionShape2D = $TriggerArea/TriggerCollision |  | ||||||
| @onready var timer: Timer = $Timer |  | ||||||
| @onready var attack_path: Path2D = $AttackPath |  | ||||||
| @onready var path_follow_2d: PathFollow2D = $AttackPath/PathFollow2D |  | ||||||
| @onready var attack_area: Area2D = $AttackPath/PathFollow2D/Sprite2D/AttackArea |  | ||||||
|  |  | ||||||
| var base_damage: float = 5.0 |  | ||||||
| var current_target: Node2D = null |  | ||||||
| var current_progress: float = 0.0 |  | ||||||
| var damaged_this_attack: Array = [] |  | ||||||
| var is_attacking: bool = false |  | ||||||
|  |  | ||||||
|  |  | ||||||
| func _ready() -> void: |  | ||||||
| 	attack_path.visible = false |  | ||||||
|  |  | ||||||
|  |  | ||||||
| func _process(delta: float) -> void: |  | ||||||
| 	if timer.is_stopped() and current_progress == 0.0: |  | ||||||
| 		timer.start() |  | ||||||
| 	if current_progress > 0.95: |  | ||||||
| 		reset_attack() |  | ||||||
| 	if ( |  | ||||||
| 		current_target |  | ||||||
| 		and is_instance_valid(current_target) |  | ||||||
| 		and not current_target.is_queued_for_deletion() |  | ||||||
| 	): |  | ||||||
| 		track_target(current_target) |  | ||||||
| 		is_attacking = true |  | ||||||
| 		attack_path.visible = true |  | ||||||
|  |  | ||||||
| 	if is_attacking: |  | ||||||
| 		# Do attack animation |  | ||||||
| 		current_progress += delta / default_attack_time |  | ||||||
| 		current_progress = clampf(current_progress, 0.0, 1.0) |  | ||||||
| 		path_follow_2d.progress_ratio = current_progress |  | ||||||
|  |  | ||||||
|  |  | ||||||
| func reset_attack() -> void: |  | ||||||
| 	current_target = null |  | ||||||
| 	attack_path.visible = false |  | ||||||
| 	current_progress = 0.0 |  | ||||||
| 	damaged_this_attack = [] |  | ||||||
| 	is_attacking = false |  | ||||||
| 	position = Vector2.ZERO |  | ||||||
| 	rotation = 0.0 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| func set_target(body: Node2D): |  | ||||||
| 	current_target = body |  | ||||||
| 	is_attacking = true |  | ||||||
|  |  | ||||||
|  |  | ||||||
| func track_target(body: Node2D): |  | ||||||
| 	var mid_distance = attack_path.curve.get_baked_length() / 2 |  | ||||||
| 	var mid_point: Vector2 = attack_path.curve.sample_baked(mid_distance) |  | ||||||
| 	var offset = body.global_position - to_global(mid_point) |  | ||||||
|  |  | ||||||
| 	var desired_dir = (body.global_position - to_global(mid_point)).normalized() |  | ||||||
| 	var start_point_global = attack_path.to_global(attack_path.curve.sample_baked(0)) |  | ||||||
| 	var end_point_global = attack_path.to_global( |  | ||||||
| 		attack_path.curve.sample_baked(attack_path.curve.get_baked_length()) |  | ||||||
| 	) |  | ||||||
| 	var curve_dir = (start_point_global - end_point_global).normalized() |  | ||||||
| 	var angle_diff = curve_dir.angle_to(desired_dir) |  | ||||||
| 	if rotation == 0.0: |  | ||||||
| 		rotation = curve_dir.angle_to(desired_dir) |  | ||||||
| 	position += offset |  | ||||||
|  |  | ||||||
|  |  | ||||||
| func _on_timer_timeout() -> void: |  | ||||||
| 	if current_target: |  | ||||||
| 		if trigger_area.has_overlapping_areas(): |  | ||||||
| 			if current_target not in trigger_area.get_overlapping_bodies(): |  | ||||||
| 				current_target = null |  | ||||||
| 		return |  | ||||||
| 	if trigger_area.has_overlapping_bodies(): |  | ||||||
| 		for body in trigger_area.get_overlapping_bodies(): |  | ||||||
| 			if body.is_in_group(GlobalConst.GROUP_ENEMY): |  | ||||||
| 				set_target(body) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| func _on_attack_area_body_entered(body: Node2D) -> void: |  | ||||||
| 	if not attack_path.visible: |  | ||||||
| 		return |  | ||||||
| 	if body in damaged_this_attack: |  | ||||||
| 		return |  | ||||||
| 	if body.is_in_group(GlobalConst.GROUP_ENEMY) and body.is_in_group(GlobalConst.GROUP_DAMAGEABLE): |  | ||||||
| 		var crit_chance = player.player_stats.get_final("crit_chance", player.modifiers) |  | ||||||
| 		var damage_dealt = base_damage |  | ||||||
| 		var is_crit = randf() >= 1 - crit_chance |  | ||||||
| 		if is_crit: |  | ||||||
| 			damage_dealt *= player.player_stats.get_final("crit_multiplier", player.modifiers) |  | ||||||
|  |  | ||||||
| 		body.take_damage(damage_dealt, is_crit) |  | ||||||
| 		damaged_this_attack.append(body) |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| uid://db326gu8abue5 |  | ||||||
| @@ -1,53 +0,0 @@ | |||||||
| [gd_scene load_steps=6 format=3 uid="uid://cdojqe2m4kxx1"] |  | ||||||
|  |  | ||||||
| [ext_resource type="Texture2D" uid="uid://dycw7c3484dir" path="res://assets/sprites/sword.png" id="1_3fwwl"] |  | ||||||
| [ext_resource type="Script" uid="uid://db326gu8abue5" path="res://scenes/attacks/attack_sword.gd" id="1_frsqi"] |  | ||||||
|  |  | ||||||
| [sub_resource type="Curve2D" id="Curve2D_frsqi"] |  | ||||||
| bake_interval = 2.0 |  | ||||||
| _data = { |  | ||||||
| "points": PackedVector2Array(0, 0, 0, 0, 0, 0, 0, 0, 25, 10, 200, 0) |  | ||||||
| } |  | ||||||
| point_count = 2 |  | ||||||
|  |  | ||||||
| [sub_resource type="RectangleShape2D" id="RectangleShape2D_frsqi"] |  | ||||||
| size = Vector2(13.9997, 46.999) |  | ||||||
|  |  | ||||||
| [sub_resource type="CircleShape2D" id="CircleShape2D_3fwwl"] |  | ||||||
| radius = 267.002 |  | ||||||
|  |  | ||||||
| [node name="AttackSword" type="Node2D"] |  | ||||||
| script = ExtResource("1_frsqi") |  | ||||||
|  |  | ||||||
| [node name="AttackPath" type="Path2D" parent="."] |  | ||||||
| curve = SubResource("Curve2D_frsqi") |  | ||||||
|  |  | ||||||
| [node name="PathFollow2D" type="PathFollow2D" parent="AttackPath"] |  | ||||||
| loop = false |  | ||||||
|  |  | ||||||
| [node name="Sprite2D" type="Sprite2D" parent="AttackPath/PathFollow2D"] |  | ||||||
| position = Vector2(0.322462, -0.946582) |  | ||||||
| rotation = 0.328329 |  | ||||||
| texture = ExtResource("1_3fwwl") |  | ||||||
|  |  | ||||||
| [node name="AttackArea" type="Area2D" parent="AttackPath/PathFollow2D/Sprite2D"] |  | ||||||
| collision_layer = 0 |  | ||||||
| collision_mask = 2 |  | ||||||
|  |  | ||||||
| [node name="AttackCollision" type="CollisionShape2D" parent="AttackPath/PathFollow2D/Sprite2D/AttackArea"] |  | ||||||
| position = Vector2(-0.0328934, -4.50646) |  | ||||||
| shape = SubResource("RectangleShape2D_frsqi") |  | ||||||
|  |  | ||||||
| [node name="TriggerArea" type="Area2D" parent="."] |  | ||||||
| visible = false |  | ||||||
| collision_layer = 0 |  | ||||||
| collision_mask = 2 |  | ||||||
|  |  | ||||||
| [node name="TriggerCollision" type="CollisionShape2D" parent="TriggerArea"] |  | ||||||
| shape = SubResource("CircleShape2D_3fwwl") |  | ||||||
|  |  | ||||||
| [node name="Timer" type="Timer" parent="."] |  | ||||||
| one_shot = true |  | ||||||
|  |  | ||||||
| [connection signal="body_entered" from="AttackPath/PathFollow2D/Sprite2D/AttackArea" to="." method="_on_attack_area_body_entered"] |  | ||||||
| [connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] |  | ||||||
| @@ -55,6 +55,7 @@ func do_movement(delta: float) -> void: | |||||||
| 	else: | 	else: | ||||||
| 		_do_nav_agent_movement() | 		_do_nav_agent_movement() | ||||||
|  |  | ||||||
|  |  | ||||||
| func _has_direct_path(): | func _has_direct_path(): | ||||||
| 	target_cast.target_position = to_local(target.global_position) | 	target_cast.target_position = to_local(target.global_position) | ||||||
| 	target_cast.enabled = true | 	target_cast.enabled = true | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ func _process(delta: float) -> void: | |||||||
| 	elapsed_time += delta | 	elapsed_time += delta | ||||||
| 	main_ui.player_ui.set_elapsed_time(elapsed_time) | 	main_ui.player_ui.set_elapsed_time(elapsed_time) | ||||||
|  |  | ||||||
|  |  | ||||||
| func _unhandled_input(event: InputEvent) -> void: | func _unhandled_input(event: InputEvent) -> void: | ||||||
| 	if event.is_action_pressed("ui_cancel"): | 	if event.is_action_pressed("ui_cancel"): | ||||||
| 		main_ui.pause_ui.toggle_pause_ui() | 		main_ui.pause_ui.toggle_pause_ui() | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| [gd_scene load_steps=8 format=3 uid="uid://bjg50n7aab3ng"] | [gd_scene load_steps=9 format=3 uid="uid://bjg50n7aab3ng"] | ||||||
|  |  | ||||||
| [ext_resource type="Script" uid="uid://brb4ssksmtq8k" path="res://scenes/main.gd" id="1_jyhfs"] | [ext_resource type="Script" uid="uid://brb4ssksmtq8k" path="res://scenes/main.gd" id="1_jyhfs"] | ||||||
| [ext_resource type="PackedScene" uid="uid://4xha2nhf8fya" path="res://scenes/test_level.tscn" id="1_o5qli"] | [ext_resource type="PackedScene" uid="uid://4xha2nhf8fya" path="res://scenes/test_level.tscn" id="1_o5qli"] | ||||||
| @@ -7,6 +7,7 @@ | |||||||
| [ext_resource type="PackedScene" uid="uid://dy73qrxcgrwg3" path="res://scenes/managers/enemy_manager.tscn" id="5_tbgi4"] | [ext_resource type="PackedScene" uid="uid://dy73qrxcgrwg3" path="res://scenes/managers/enemy_manager.tscn" id="5_tbgi4"] | ||||||
| [ext_resource type="PackedScene" uid="uid://bbev8m5g0p3a3" path="res://scenes/pickups/pickup_magnet.tscn" id="6_tefeu"] | [ext_resource type="PackedScene" uid="uid://bbev8m5g0p3a3" path="res://scenes/pickups/pickup_magnet.tscn" id="6_tefeu"] | ||||||
| [ext_resource type="PackedScene" uid="uid://cr8gj1dlloamp" path="res://scenes/pickups/pickup_hp.tscn" id="7_o6xl0"] | [ext_resource type="PackedScene" uid="uid://cr8gj1dlloamp" path="res://scenes/pickups/pickup_hp.tscn" id="7_o6xl0"] | ||||||
|  | [ext_resource type="PackedScene" uid="uid://c2o1lpm4pimpr" path="res://scenes/managers/projectile_manager.tscn" id="8_tipki"] | ||||||
|  |  | ||||||
| [node name="Main" type="Node2D"] | [node name="Main" type="Node2D"] | ||||||
| script = ExtResource("1_jyhfs") | script = ExtResource("1_jyhfs") | ||||||
| @@ -32,3 +33,5 @@ position = Vector2(1697, 414) | |||||||
|  |  | ||||||
| [node name="PickupHP" parent="." instance=ExtResource("7_o6xl0")] | [node name="PickupHP" parent="." instance=ExtResource("7_o6xl0")] | ||||||
| position = Vector2(1678, 939) | position = Vector2(1678, 939) | ||||||
|  |  | ||||||
|  | [node name="ProjectileManager" parent="." instance=ExtResource("8_tipki")] | ||||||
|   | |||||||
| @@ -26,11 +26,13 @@ func _on_timer_timeout() -> void: | |||||||
| 		new_enemy.target = target | 		new_enemy.target = target | ||||||
| 		add_child(new_enemy) | 		add_child(new_enemy) | ||||||
|  |  | ||||||
|  |  | ||||||
| func _on_stop_spawning(val: bool): | func _on_stop_spawning(val: bool): | ||||||
| 	if val: | 	if val: | ||||||
| 		timer.stop() | 		timer.stop() | ||||||
| 	elif timer.is_stopped(): | 	elif timer.is_stopped(): | ||||||
| 		timer.start() | 		timer.start() | ||||||
|  |  | ||||||
|  |  | ||||||
| func _on_set_spawn_rate(val: float): | func _on_set_spawn_rate(val: float): | ||||||
| 	timer.wait_time = 1 / val | 	timer.wait_time = 1 / val | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								scenes/managers/projectile_manager.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								scenes/managers/projectile_manager.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | [gd_scene format=3 uid="uid://c2o1lpm4pimpr"] | ||||||
|  |  | ||||||
|  | [node name="ProjectileManager" type="Node2D"] | ||||||
| @@ -5,7 +5,6 @@ extends CharacterBody2D | |||||||
| @export var main_ui: MainUI | @export var main_ui: MainUI | ||||||
|  |  | ||||||
| @onready var sprite_2d: Sprite2D = $Sprite2D | @onready var sprite_2d: Sprite2D = $Sprite2D | ||||||
| @onready var attack_sword: Node2D = $AttackSword |  | ||||||
|  |  | ||||||
| var player_stats: PlayerStats = PlayerStats.new() | var player_stats: PlayerStats = PlayerStats.new() | ||||||
| var modifiers: Array[PlayerStatsModifier] = [] | var modifiers: Array[PlayerStatsModifier] = [] | ||||||
| @@ -61,7 +60,6 @@ func die(): | |||||||
| 	dead = true | 	dead = true | ||||||
| 	remove_from_group("damagable") | 	remove_from_group("damagable") | ||||||
| 	get_tree().call_group("enemy", "cheer") | 	get_tree().call_group("enemy", "cheer") | ||||||
| 	attack_sword.queue_free() |  | ||||||
| 	GlobalConst.sig_stop_spawning.emit(true) | 	GlobalConst.sig_stop_spawning.emit(true) | ||||||
| 	sprite_2d.z_index += 10 | 	sprite_2d.z_index += 10 | ||||||
| 	get_taunted() | 	get_taunted() | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
| [ext_resource type="Texture2D" uid="uid://5x5wimok8uw2" path="res://assets/sprites/roguelikeChar_transparent.png" id="1_3vyb7"] | [ext_resource type="Texture2D" uid="uid://5x5wimok8uw2" path="res://assets/sprites/roguelikeChar_transparent.png" id="1_3vyb7"] | ||||||
| [ext_resource type="Script" uid="uid://cvqaxckx4num3" path="res://scenes/player.gd" id="1_g2els"] | [ext_resource type="Script" uid="uid://cvqaxckx4num3" path="res://scenes/player.gd" id="1_g2els"] | ||||||
| [ext_resource type="PackedScene" uid="uid://cdojqe2m4kxx1" path="res://scenes/attacks/attack_sword.tscn" id="3_qhqgy"] | [ext_resource type="PackedScene" uid="uid://dfikvj27k01tu" path="res://scenes/weapons/weapon_sword.tscn" id="3_qhqgy"] | ||||||
|  |  | ||||||
| [sub_resource type="CircleShape2D" id="CircleShape2D_3vyb7"] | [sub_resource type="CircleShape2D" id="CircleShape2D_3vyb7"] | ||||||
| radius = 8.0 | radius = 8.0 | ||||||
| @@ -22,9 +22,6 @@ region_rect = Rect2(0, 104, 16, 14) | |||||||
| [node name="CollisionShape2D" type="CollisionShape2D" parent="."] | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] | ||||||
| shape = SubResource("CircleShape2D_3vyb7") | shape = SubResource("CircleShape2D_3vyb7") | ||||||
|  |  | ||||||
| [node name="AttackSword" parent="." node_paths=PackedStringArray("player") instance=ExtResource("3_qhqgy")] |  | ||||||
| player = NodePath("..") |  | ||||||
|  |  | ||||||
| [node name="PickupArea" type="Area2D" parent="."] | [node name="PickupArea" type="Area2D" parent="."] | ||||||
| collision_layer = 0 | collision_layer = 0 | ||||||
| collision_mask = 24 | collision_mask = 24 | ||||||
| @@ -33,4 +30,6 @@ monitorable = false | |||||||
| [node name="CollisionShape2D" type="CollisionShape2D" parent="PickupArea"] | [node name="CollisionShape2D" type="CollisionShape2D" parent="PickupArea"] | ||||||
| shape = SubResource("CircleShape2D_qhqgy") | shape = SubResource("CircleShape2D_qhqgy") | ||||||
|  |  | ||||||
|  | [node name="WeaponSword" parent="." instance=ExtResource("3_qhqgy")] | ||||||
|  |  | ||||||
| [connection signal="area_entered" from="PickupArea" to="." method="_on_pickup_area_area_entered"] | [connection signal="area_entered" from="PickupArea" to="." method="_on_pickup_area_area_entered"] | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								scenes/weapons/weapon_base.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								scenes/weapons/weapon_base.gd
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | class_name WeaponBase | ||||||
|  | extends Node2D | ||||||
|  |  | ||||||
|  | @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 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func _on_attack_cd_timer_timeout() -> void: | ||||||
|  | 	do_attack() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func do_attack() -> void: | ||||||
|  | 	push_error("%s does not implement do_attack" % 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 | ||||||
							
								
								
									
										1
									
								
								scenes/weapons/weapon_base.gd.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								scenes/weapons/weapon_base.gd.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | uid://d6nwfhyethdw | ||||||
							
								
								
									
										11
									
								
								scenes/weapons/weapon_base.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								scenes/weapons/weapon_base.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | [gd_scene load_steps=2 format=3 uid="uid://i8mtky2req41"] | ||||||
|  |  | ||||||
|  | [ext_resource type="Script" uid="uid://d6nwfhyethdw" path="res://scenes/weapons/weapon_base.gd" id="1_v4xn6"] | ||||||
|  |  | ||||||
|  | [node name="WeaponBase" type="Node2D"] | ||||||
|  | script = ExtResource("1_v4xn6") | ||||||
|  |  | ||||||
|  | [node name="AttackCDTimer" type="Timer" parent="."] | ||||||
|  | autostart = true | ||||||
|  |  | ||||||
|  | [connection signal="timeout" from="AttackCDTimer" to="." method="_on_attack_cd_timer_timeout"] | ||||||
							
								
								
									
										42
									
								
								scenes/weapons/weapon_sword.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								scenes/weapons/weapon_sword.gd
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | class_name WeaponSword | ||||||
|  | extends WeaponBase | ||||||
|  |  | ||||||
|  | const WEAPON_SWORD_PROJECTILE = preload("res://scenes/weapons/weapon_sword_projectile.tscn") | ||||||
|  |  | ||||||
|  | @onready var targeting_range: Area2D = $TargetingRange | ||||||
|  | @onready var targeting_range_shape: CollisionShape2D = $TargetingRange/CollisionShape2D | ||||||
|  |  | ||||||
|  | signal projectile_hit(projectile: WeaponSwordProjectile, enemy: EnemyBase) | ||||||
|  |  | ||||||
|  | var _player: Player | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func _ready() -> void: | ||||||
|  | 	targeting_range_shape.shape.radius = attack_range | ||||||
|  | 	projectile_hit.connect(_on_projectile_hit) | ||||||
|  | 	_player = get_tree().get_first_node_in_group(GlobalConst.GROUP_PLAYER) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func do_attack() -> void: | ||||||
|  | 	var target: EnemyBase = find_target_in_radius() | ||||||
|  | 	if not target: | ||||||
|  | 		return | ||||||
|  |  | ||||||
|  | 	var projectile = WEAPON_SWORD_PROJECTILE.instantiate() | ||||||
|  | 	projectile.damage = attack_damage | ||||||
|  | 	projectile.target = target | ||||||
|  | 	projectile.on_hit_sig = projectile_hit | ||||||
|  | 	add_child(projectile) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func deal_damage(enemy: EnemyBase): | ||||||
|  | 	var crit_chance = _player.player_stats.get_final("crit_chance", _player.modifiers) | ||||||
|  | 	var damage_dealt = attack_damage | ||||||
|  | 	var is_crit = randf() >= 1 - crit_chance | ||||||
|  | 	if is_crit: | ||||||
|  | 		damage_dealt *= _player.player_stats.get_final("crit_multiplier", _player.modifiers) | ||||||
|  | 	enemy.take_damage(damage_dealt, is_crit) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func _on_projectile_hit(projectile: WeaponSwordProjectile, enemy: EnemyBase): | ||||||
|  | 	deal_damage(enemy) | ||||||
							
								
								
									
										1
									
								
								scenes/weapons/weapon_sword.gd.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								scenes/weapons/weapon_sword.gd.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | uid://b072d866r4usq | ||||||
							
								
								
									
										22
									
								
								scenes/weapons/weapon_sword.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								scenes/weapons/weapon_sword.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | [gd_scene load_steps=4 format=3 uid="uid://dfikvj27k01tu"] | ||||||
|  |  | ||||||
|  | [ext_resource type="PackedScene" uid="uid://i8mtky2req41" path="res://scenes/weapons/weapon_base.tscn" id="1_2dti5"] | ||||||
|  | [ext_resource type="Script" uid="uid://b072d866r4usq" path="res://scenes/weapons/weapon_sword.gd" id="2_ruf80"] | ||||||
|  |  | ||||||
|  | [sub_resource type="CircleShape2D" id="CircleShape2D_ruf80"] | ||||||
|  |  | ||||||
|  | [node name="WeaponSword" instance=ExtResource("1_2dti5")] | ||||||
|  | script = ExtResource("2_ruf80") | ||||||
|  | attack_cd = 1.0 | ||||||
|  | attack_damage = 5.0 | ||||||
|  | attack_aoe = 1.0 | ||||||
|  | attack_duration = 1.0 | ||||||
|  | attack_range = 150.0 | ||||||
|  |  | ||||||
|  | [node name="TargetingRange" type="Area2D" parent="." index="1"] | ||||||
|  | collision_layer = 0 | ||||||
|  | collision_mask = 2 | ||||||
|  | monitorable = false | ||||||
|  |  | ||||||
|  | [node name="CollisionShape2D" type="CollisionShape2D" parent="TargetingRange" index="0"] | ||||||
|  | shape = SubResource("CircleShape2D_ruf80") | ||||||
							
								
								
									
										40
									
								
								scenes/weapons/weapon_sword_projectile.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								scenes/weapons/weapon_sword_projectile.gd
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | class_name WeaponSwordProjectile | ||||||
|  | extends Node2D | ||||||
|  |  | ||||||
|  | @export var speed: float = 500.0 | ||||||
|  | @export var range: float = 200.0 | ||||||
|  | @export var target: Node2D | ||||||
|  | @export var damage: float | ||||||
|  | @export var on_hit_sig: Signal | ||||||
|  |  | ||||||
|  | var _direction: Vector2 | ||||||
|  | var _traveled: float = 0.0 | ||||||
|  | var _already_hit: Array[PhysicsBody2D] = [] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func _ready() -> void: | ||||||
|  | 	_direction = global_position.direction_to(target.global_position).normalized() | ||||||
|  | 	_traveled = 0.0 | ||||||
|  | 	rotation = _direction.angle() | ||||||
|  | 	if _direction.x < 0: | ||||||
|  | 		rotation += PI | ||||||
|  | 	var proj_manager = get_tree().get_first_node_in_group(GlobalConst.GROUP_PROJ_MANAGER) | ||||||
|  | 	if not proj_manager: | ||||||
|  | 		return | ||||||
|  | 	reparent(proj_manager, true) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func _physics_process(delta: float) -> void: | ||||||
|  | 	var step = _direction * speed * delta | ||||||
|  | 	global_position += step | ||||||
|  | 	_traveled += step.length() | ||||||
|  |  | ||||||
|  | 	if _traveled >= range: | ||||||
|  | 		queue_free() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | func _on_area_2d_body_entered(body: Node2D) -> void: | ||||||
|  | 	if body in _already_hit: | ||||||
|  | 		return | ||||||
|  | 	on_hit_sig.emit(self, body) | ||||||
|  | 	_already_hit.append(body) | ||||||
							
								
								
									
										1
									
								
								scenes/weapons/weapon_sword_projectile.gd.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								scenes/weapons/weapon_sword_projectile.gd.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | uid://c40iaqdubwl0p | ||||||
							
								
								
									
										24
									
								
								scenes/weapons/weapon_sword_projectile.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								scenes/weapons/weapon_sword_projectile.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | [gd_scene load_steps=4 format=3 uid="uid://bv5f47x3ishmf"] | ||||||
|  |  | ||||||
|  | [ext_resource type="Script" uid="uid://c40iaqdubwl0p" path="res://scenes/weapons/weapon_sword_projectile.gd" id="1_asuu4"] | ||||||
|  | [ext_resource type="Texture2D" uid="uid://dycw7c3484dir" path="res://assets/sprites/sword.png" id="2_pxap4"] | ||||||
|  |  | ||||||
|  | [sub_resource type="RectangleShape2D" id="RectangleShape2D_ygc1t"] | ||||||
|  | size = Vector2(16, 46) | ||||||
|  |  | ||||||
|  | [node name="WeaponSwordProjectile" type="Node2D"] | ||||||
|  | script = ExtResource("1_asuu4") | ||||||
|  |  | ||||||
|  | [node name="Sprite2D" type="Sprite2D" parent="."] | ||||||
|  | texture = ExtResource("2_pxap4") | ||||||
|  |  | ||||||
|  | [node name="Area2D" type="Area2D" parent="."] | ||||||
|  | collision_layer = 0 | ||||||
|  | collision_mask = 2 | ||||||
|  | monitorable = false | ||||||
|  |  | ||||||
|  | [node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] | ||||||
|  | position = Vector2(0, -5) | ||||||
|  | shape = SubResource("RectangleShape2D_ygc1t") | ||||||
|  |  | ||||||
|  | [connection signal="body_entered" from="Area2D" to="." method="_on_area_2d_body_entered"] | ||||||
		Reference in New Issue
	
	Block a user