From e4893a5cc6fd629374a829484ed5e788d63033d9 Mon Sep 17 00:00:00 2001 From: ToasterPanic Date: Sat, 6 Dec 2025 20:48:11 -0500 Subject: [PATCH] Basic ground enemy AI, crosshair + laser for aiming --- scenes/ground.tscn | 48 ++-------- scenes/locations/space_station_1.tscn | 131 +++++++++++++++++++++++++- scripts/enemy_ground.gd | 125 ++++++++++++++++++++---- scripts/player_ground.gd | 10 ++ textures/crosshair.png | Bin 0 -> 161 bytes textures/crosshair.png.import | 40 ++++++++ 6 files changed, 299 insertions(+), 55 deletions(-) create mode 100644 textures/crosshair.png create mode 100644 textures/crosshair.png.import diff --git a/scenes/ground.tscn b/scenes/ground.tscn index 2155d0b..27953cf 100644 --- a/scenes/ground.tscn +++ b/scenes/ground.tscn @@ -6,7 +6,7 @@ [ext_resource type="Texture2D" uid="uid://c0cyhybh30ogt" path="res://textures/player-ground-sheet.png" id="4_2c1ag"] [ext_resource type="Texture2D" uid="uid://csrlh1sbdroud" path="res://textures/pistol.png" id="5_8gbjj"] [ext_resource type="AudioStream" uid="uid://5x8fl2mk082h" path="res://sounds/gunshot_1.mp3" id="6_2c1ag"] -[ext_resource type="Script" uid="uid://bv7ymrwe6ciax" path="res://scripts/enemy_ground.gd" id="7_modao"] +[ext_resource type="Texture2D" uid="uid://coflscl1w4vtr" path="res://textures/crosshair.png" id="7_modao"] [ext_resource type="Shader" uid="uid://d3hoh7ec2w8q7" path="res://scripts/outline.gdshader" id="7_vuhkc"] [ext_resource type="Shader" uid="uid://d2e0541hawkml" path="res://scripts/vignette.gdshader" id="9_5vwr8"] [ext_resource type="Shader" uid="uid://bk7q00br1ms30" path="res://scripts/retro.gdshader" id="9_257nh"] @@ -160,6 +160,15 @@ texture = ExtResource("5_8gbjj") [node name="Gunshot" type="AudioStreamPlayer2D" parent="PlayerGround/HeldItem"] stream = ExtResource("6_2c1ag") +[node name="Crosshair" type="Sprite2D" parent="PlayerGround/HeldItem"] +scale = Vector2(2, 2) +texture = ExtResource("7_modao") + +[node name="Line" type="Line2D" parent="PlayerGround/HeldItem"] +points = PackedVector2Array(0, 0, 512, 0) +width = 2.0 +default_color = Color(1, 0, 0, 1) + [node name="Hitbox" type="Area2D" parent="PlayerGround"] collision_layer = 2 collision_mask = 2 @@ -168,43 +177,6 @@ collision_mask = 2 shape = SubResource("CapsuleShape2D_8gbjj") debug_color = Color(1, 0, 0, 0.41960785) -[node name="EnemyTest" type="CharacterBody2D" parent="."] -position = Vector2(349, -58) -script = ExtResource("7_modao") - -[node name="Sprite" type="AnimatedSprite2D" parent="EnemyTest"] -scale = Vector2(2, 2) -sprite_frames = SubResource("SpriteFrames_aergo") - -[node name="Camera" type="Camera2D" parent="EnemyTest"] -position_smoothing_enabled = true - -[node name="CollisionShape" type="CollisionShape2D" parent="EnemyTest"] -position = Vector2(0, 40) -shape = SubResource("CapsuleShape2D_176r3") - -[node name="HeldItem" type="Node2D" parent="EnemyTest"] - -[node name="Cast" type="RayCast2D" parent="EnemyTest/HeldItem"] -target_position = Vector2(2048, 0) -collision_mask = 2 -collide_with_areas = true - -[node name="Sprite" type="Sprite2D" parent="EnemyTest/HeldItem"] -position = Vector2(56, 0) -texture = ExtResource("5_8gbjj") - -[node name="Gunshot" type="AudioStreamPlayer2D" parent="EnemyTest/HeldItem"] -stream = ExtResource("6_2c1ag") - -[node name="Hitbox" type="Area2D" parent="EnemyTest"] -collision_layer = 2 -collision_mask = 2 - -[node name="CollisionShape" type="CollisionShape2D" parent="EnemyTest/Hitbox"] -shape = SubResource("CapsuleShape2D_8gbjj") -debug_color = Color(1, 0, 0, 0.41960785) - [node name="UI" type="CanvasLayer" parent="."] [node name="Vignette" type="ColorRect" parent="UI"] diff --git a/scenes/locations/space_station_1.tscn b/scenes/locations/space_station_1.tscn index 7d65cd2..48eedb0 100644 --- a/scenes/locations/space_station_1.tscn +++ b/scenes/locations/space_station_1.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=47 format=4 uid="uid://dfjnikjjynj0e"] +[gd_scene load_steps=57 format=4 uid="uid://dfjnikjjynj0e"] [ext_resource type="AudioStream" uid="uid://dgv01wy8r7ej2" path="res://sounds/uglyburger.mp3" id="1_kpeax"] [ext_resource type="Texture2D" uid="uid://btcap3oh2dqt8" path="res://textures/wall_tile.png" id="2_4uppp"] @@ -20,6 +20,9 @@ [ext_resource type="Texture2D" uid="uid://ktvnppfhchoj" path="res://textures/bed.png" id="17_50pdk"] [ext_resource type="Texture2D" uid="uid://cmv4wou5glrl7" path="res://textures/bed_foot.png" id="18_83fjc"] [ext_resource type="Script" uid="uid://cva4b60iqolqy" path="res://scripts/story_handler_1.gd" id="19_akl5n"] +[ext_resource type="Script" uid="uid://bv7ymrwe6ciax" path="res://scripts/enemy_ground.gd" id="21_tvsp8"] +[ext_resource type="Texture2D" uid="uid://csrlh1sbdroud" path="res://textures/pistol.png" id="22_6l1ru"] +[ext_resource type="AudioStream" uid="uid://5x8fl2mk082h" path="res://sounds/gunshot_1.mp3" id="23_janyw"] [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_vmimc"] light_mode = 1 @@ -39,36 +42,65 @@ texture = ExtResource("3_ir5n7") texture_region_size = Vector2i(32, 32) 0:0/0 = 0 +[sub_resource type="NavigationPolygon" id="NavigationPolygon_tvsp8"] +vertices = PackedVector2Array(16, 16, -16, 16, -16, -16, 16, -16) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)]) +agent_radius = 0.0 + [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_bhfm6"] texture = ExtResource("4_50pdk") texture_region_size = Vector2i(32, 32) 0:0/0 = 0 +0:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_tvsp8") + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_6l1ru"] +vertices = PackedVector2Array(16, 16, -16, 16, -16, -16, 16, -16) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)]) +agent_radius = 0.0 [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_kl2qk"] texture = ExtResource("5_83fjc") texture_region_size = Vector2i(32, 32) 0:0/0 = 0 +0:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_6l1ru") [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_ga4dw"] texture = ExtResource("6_akl5n") texture_region_size = Vector2i(32, 32) 0:0/0 = 0 +[sub_resource type="NavigationPolygon" id="NavigationPolygon_janyw"] +vertices = PackedVector2Array(16, 16, -16, 16, -16, -16, 16, -16) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)]) +agent_radius = 0.0 + [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_ir5n7"] texture = ExtResource("7_4uppp") texture_region_size = Vector2i(32, 32) 0:0/0 = 0 +0:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_janyw") + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_vmpfq"] +vertices = PackedVector2Array(16, 16, -16, 16, -16, -16, 16, -16) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)]) +agent_radius = 0.0 [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_50pdk"] texture = ExtResource("8_ir5n7") texture_region_size = Vector2i(32, 32) 0:0/0 = 0 +0:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_vmpfq") [sub_resource type="TileSet" id="TileSet_uxsmb"] tile_size = Vector2i(32, 32) occlusion_layer_0/light_mask = 1 physics_layer_0/collision_layer = 3 physics_layer_0/collision_mask = 3 +navigation_layer_0/layers = 1 sources/0 = SubResource("TileSetAtlasSource_k741b") sources/1 = SubResource("TileSetAtlasSource_mdep3") sources/2 = SubResource("TileSetAtlasSource_bhfm6") @@ -200,6 +232,59 @@ size = Vector2(40, 40) [sub_resource type="RectangleShape2D" id="RectangleShape2D_83fjc"] size = Vector2(61.5, 21) +[sub_resource type="SpriteFrames" id="SpriteFrames_vmpfq"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_jo68p") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_6gpfv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_fd1o3") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_yqhs4") +}], +"loop": true, +"name": &"default", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_jo68p") +}], +"loop": true, +"name": &"idle", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_6gpfv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jo68p") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_fd1o3") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_yqhs4") +}], +"loop": true, +"name": &"walk", +"speed": 7.0 +}] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_jxmby"] +radius = 21.0 +height = 48.0 + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_tvsp8"] +radius = 25.0 +height = 148.0 + [node name="SpaceStation1" type="Node2D"] [node name="Uglyburger" type="AudioStreamPlayer" parent="."] @@ -356,6 +441,50 @@ texture = ExtResource("18_83fjc") position = Vector2(-0.25, 37.5) shape = SubResource("RectangleShape2D_83fjc") +[node name="EnemyTest" type="CharacterBody2D" parent="."] +position = Vector2(-162, -537) +script = ExtResource("21_tvsp8") + +[node name="Sprite" type="AnimatedSprite2D" parent="EnemyTest"] +scale = Vector2(2, 2) +sprite_frames = SubResource("SpriteFrames_vmpfq") + +[node name="CollisionShape" type="CollisionShape2D" parent="EnemyTest"] +position = Vector2(0, 40) +shape = SubResource("CapsuleShape2D_jxmby") + +[node name="LineOfSight" type="RayCast2D" parent="EnemyTest"] +target_position = Vector2(2048, 0) +collision_mask = 2 +collide_with_areas = true + +[node name="HeldItem" type="Node2D" parent="EnemyTest"] + +[node name="Cast" type="RayCast2D" parent="EnemyTest/HeldItem"] +target_position = Vector2(2048, 0) +collision_mask = 2 +collide_with_areas = true + +[node name="Sprite" type="Sprite2D" parent="EnemyTest/HeldItem"] +position = Vector2(56, 0) +texture = ExtResource("22_6l1ru") + +[node name="Gunshot" type="AudioStreamPlayer2D" parent="EnemyTest/HeldItem"] +stream = ExtResource("23_janyw") + +[node name="Hitbox" type="Area2D" parent="EnemyTest"] +collision_layer = 2 +collision_mask = 2 + +[node name="CollisionShape" type="CollisionShape2D" parent="EnemyTest/Hitbox"] +shape = SubResource("CapsuleShape2D_tvsp8") +debug_color = Color(1, 0, 0, 0.41960785) + +[node name="Navagent" type="NavigationAgent2D" parent="EnemyTest"] +path_desired_distance = 34.0 +target_desired_distance = 34.0 +path_postprocessing = 1 + [connection signal="body_entered" from="GalactamartWorker/InteractArea" to="GalactamartWorker/InteractArea" method="_on_body_entered"] [connection signal="body_exited" from="GalactamartWorker/InteractArea" to="GalactamartWorker/InteractArea" method="_on_body_exited"] [connection signal="body_entered" from="ExitShip/InteractArea" to="ExitShip/InteractArea" method="_on_body_entered"] diff --git a/scripts/enemy_ground.gd b/scripts/enemy_ground.gd index b864c2b..7c2c357 100644 --- a/scripts/enemy_ground.gd +++ b/scripts/enemy_ground.gd @@ -1,32 +1,125 @@ extends "res://scripts/character_ground.gd" var last_aim_direction = Vector2(0, 0) +var alerted = false + +var inaccuracy = 15 + +var reaction_time = 0.35 +var reaction_halve_distance = 720 +var reaction_timer = 0 + +enum { + AI_MODE_IDLE, + AI_MODE_ATTACK, + AI_MODE_SEARCH +} + +enum { + AI_STATE_DEFAULT, + AI_STATE_CHASE, + AI_STATE_CHASE_LAST_SEEN +} + +var ai_mode = AI_MODE_IDLE +var ai_state = AI_STATE_DEFAULT + +var last_seen_player_position = null + +@onready var player = null +var game = null @export var starting_gun: String = &"pistol" +@export var starting_health: int = 50 + func _ready() -> void: super() + health = starting_health + set_ground_gun(starting_gun) func _process(delta: float) -> void: super(delta) - if input_icon.using_gamepad: - if !((Input.get_axis("aim_left", "aim_right") == 0) and (Input.get_axis("aim_up", "aim_down") == 0)): - $HeldItem.rotation = Vector2( - Input.get_axis("aim_left", "aim_right"), - Input.get_axis("aim_up", "aim_down") - ).angle() + if !player: + if game: + player = game.get_node("PlayerGround") + else: return + + if ai_mode == AI_MODE_IDLE: + $LineOfSight.look_at(player.global_position) - if ($HeldItem.rotation_degrees < -90) or ($HeldItem.rotation_degrees > 90): - $HeldItem/Sprite.scale.y = -1 + if player.is_ancestor_of($LineOfSight.get_collider()): + var divider = clamp((player.global_position - global_position).length() / reaction_halve_distance, 1, 8) * 1.5 + reaction_timer += delta / divider + + if reaction_time < reaction_timer: + ai_mode = AI_MODE_ATTACK + reaction_timer = 0 else: - $HeldItem/Sprite.scale.y = 1 - else: - $HeldItem.look_at(get_global_mouse_position()) - - horizontial_movement = Input.get_axis("ground_left", "ground_right") - vertical_movement = Input.get_axis("ground_up", "ground_down") - - firing = Input.is_action_pressed("fire") + reaction_timer -= delta + + if reaction_timer < 0: + reaction_timer = 0 + + + elif ai_mode == AI_MODE_ATTACK: + $LineOfSight.look_at(player.global_position) + if player.is_ancestor_of($LineOfSight.get_collider()): + if !$Navagent.target_position or ($Navagent.target_position != player.global_position): + $Navagent.target_position = player.global_position + + ai_state = AI_STATE_CHASE + elif last_seen_player_position: + if !$Navagent.target_position or ($Navagent.target_position != last_seen_player_position): + $Navagent.target_position = last_seen_player_position + + ai_state = AI_STATE_CHASE_LAST_SEEN + + $HeldItem.look_at(player.global_position) + + if ai_state == AI_STATE_CHASE: + if (player.global_position - global_position).length() > 256: + if $Navagent.is_navigation_finished(): + last_seen_player_position = null + ai_mode = AI_MODE_IDLE + else: + var next_position = $Navagent.get_next_path_position() + next_position.y -= 32 + var axes = global_position.direction_to(next_position) + + horizontial_movement = axes.x + vertical_movement = axes.y + + else: + horizontial_movement = 0 + vertical_movement = 0 + + reaction_timer += delta + + if reaction_timer > reaction_time: + firing = true + $HeldItem/Cast.rotation_degrees = randi_range(-inaccuracy, inaccuracy) + + reaction_timer = reaction_time + + last_seen_player_position = player.global_position + elif ai_state == AI_STATE_CHASE_LAST_SEEN: + if $Navagent.is_navigation_finished(): + last_seen_player_position = null + ai_mode = AI_MODE_IDLE + else: + var next_position = $Navagent.get_next_path_position() + next_position.y -= 32 + var axes = global_position.direction_to(next_position) + + horizontial_movement = axes.x + vertical_movement = axes.y + else: + reaction_timer -= delta + + if reaction_timer < 0: + firing = false + reaction_timer = 0 diff --git a/scripts/player_ground.gd b/scripts/player_ground.gd index c526c08..b609770 100644 --- a/scripts/player_ground.gd +++ b/scripts/player_ground.gd @@ -27,4 +27,14 @@ func _process(delta: float) -> void: horizontial_movement = Input.get_axis("ground_left", "ground_right") vertical_movement = Input.get_axis("ground_up", "ground_down") + if $HeldItem/Cast.get_collision_point(): + $HeldItem/Crosshair.visible = true + $HeldItem/Line.visible = true + + $HeldItem/Crosshair.global_position = $HeldItem/Cast.get_collision_point() + $HeldItem/Line.points[1].x = ($HeldItem/Cast.get_collision_point() - global_position).length() + else: + $HeldItem/Crosshair.visible = false + $HeldItem/Line.visible = false + firing = Input.is_action_pressed("fire") diff --git a/textures/crosshair.png b/textures/crosshair.png new file mode 100644 index 0000000000000000000000000000000000000000..683df5e36dbd854d5877f3627f186ab88d3c367b GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|qCH(4Lo9la zPCCfNpupj5`saWAHfK4fxvYZkG>UHqFN@)N^@f|NM^Uk478}=&(0T^(1M5^;8Y}Z= z{XC`rB1=oXCXL~J!?li#zq_CFDpe`6PW$