diff --git a/src/Autoloads/Steering.gd b/src/Autoloads/Steering.gd new file mode 100644 index 0000000..46c633b --- /dev/null +++ b/src/Autoloads/Steering.gd @@ -0,0 +1,24 @@ +extends Node + +const DEFAULT_MASS = 2.0 +const DEFAULT_SLOW_RADIUS = 200.0 +const DEFAULT_MAX_SPEED = 300.0 + +func arrive_to(velocity, + position_current, + position_target, + mass=DEFAULT_MASS, + slow_radius=DEFAULT_SLOW_RADIUS, + max_speed=DEFAULT_MAX_SPEED): + """ + Calculates and returns a new velocity with the arrive steering behavior arrived based on + an existing velocity (Vector2), the object's current and target positions (Vector2) + """ + var distance_to_target = position_current.distance_to(position_target) + + var desired_velocity = (position_target - position_current).normalized() * max_speed + if distance_to_target < slow_radius: + desired_velocity *= (distance_to_target / slow_radius) * .75 + .25 + var steering = (desired_velocity - velocity) / mass + + return velocity + steering diff --git a/src/Boss/SlimeBoss/SlimeBoss.tscn b/src/Boss/SlimeBoss/SlimeBoss.tscn index 88eea10..c9159f0 100644 --- a/src/Boss/SlimeBoss/SlimeBoss.tscn +++ b/src/Boss/SlimeBoss/SlimeBoss.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=23 format=2] +[gd_scene load_steps=29 format=2] [ext_resource path="res://Overlap/HurtHit_Box/Hurtbox.tscn" type="PackedScene" id=1] [ext_resource path="res://Overlap/HurtHit_Box/Hitbox.tscn" type="PackedScene" id=2] @@ -6,14 +6,12 @@ [ext_resource path="res://Boss/SlimeBoss/SlimeBossStateMachine.gd" type="Script" id=4] [ext_resource path="res://Boss/SlimeBoss/Animations/move_down.png" type="Texture" id=5] [ext_resource path="res://Boss/SlimeBoss/Animations/move_right.png" type="Texture" id=6] -[ext_resource path="res://Boss/SlimeBoss/States/Motion/Idle.gd" type="Script" id=7] -[ext_resource path="res://Boss/SlimeBoss/States/Motion/Stagger.gd" type="Script" id=8] -[ext_resource path="res://Boss/SlimeBoss/States/Motion/Wander.gd" type="Script" id=9] -[ext_resource path="res://Boss/SlimeBoss/States/Motion/SplitUp.gd" type="Script" id=10] +[ext_resource path="res://Boss/SlimeBoss/States/Roam/RoamSequence.gd" type="Script" id=7] +[ext_resource path="res://Boss/SlimeBoss/States/Roam/Wait.gd" type="Script" id=8] +[ext_resource path="res://Boss/SlimeBoss/States/FightStart.gd" type="Script" id=9] +[ext_resource path="res://Boss/SlimeBoss/Animations/move_down_angry.png" type="Texture" id=10] [ext_resource path="res://Boss/SlimeBoss/Animations/move_up.png" type="Texture" id=11] -[ext_resource path="res://Boss/SlimeBoss/States/Motion/JumpTeleport.gd" type="Script" id=12] -[ext_resource path="res://Boss/SlimeBoss/States/Motion/Dead.gd" type="Script" id=13] -[ext_resource path="res://Boss/SlimeBoss/States/Motion/Pursue.gd" type="Script" id=14] +[ext_resource path="res://Boss/SlimeBoss/States/Roam/MoveToRandomPosition.gd" type="Script" id=12] [sub_resource type="CapsuleShape2D" id=1] radius = 18.0 @@ -28,9 +26,86 @@ radius = 150.0 [sub_resource type="CapsuleShape2D" id=4] radius = 8.0 -height = 15.0 +height = 32.0 [sub_resource type="Animation" id=5] +resource_name = "FightStart" +length = 1.9 +step = 0.025 +tracks/0/type = "value" +tracks/0/path = NodePath("Sprite:texture") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0, 0.175, 0.3, 0.65, 0.775, 1.075, 1.2, 1.35, 1.9 ), +"transitions": PoolRealArray( 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +"update": 1, +"values": [ ExtResource( 5 ), ExtResource( 10 ), ExtResource( 5 ), ExtResource( 10 ), ExtResource( 5 ), ExtResource( 10 ), ExtResource( 5 ), ExtResource( 10 ), ExtResource( 5 ) ] +} +tracks/1/type = "value" +tracks/1/path = NodePath("Sprite:vframes") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ 10 ] +} +tracks/2/type = "value" +tracks/2/path = NodePath("Sprite:hframes") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ 24 ] +} +tracks/3/type = "value" +tracks/3/path = NodePath("Sprite:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ Vector2( 0, -28 ) ] +} +tracks/4/type = "value" +tracks/4/path = NodePath("Sprite:flip_h") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ false ] +} +tracks/5/type = "value" +tracks/5/path = NodePath("Sprite:frame") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/keys = { +"times": PoolRealArray( 0, 0.025, 0.05, 0.075, 0.1, 0.125, 0.15, 0.175, 0.2, 0.225, 0.25, 0.275, 0.3, 0.325, 0.35, 0.375, 0.4, 0.425, 0.45, 0.475, 0.5, 0.525, 0.55, 0.575, 0.6, 0.625, 0.65, 0.675, 0.7, 0.725, 0.75, 0.775, 0.8, 0.825, 0.85, 0.875, 0.9, 0.925, 0.95, 0.975, 1, 1.025, 1.05, 1.075, 1.1, 1.125, 1.15, 1.175, 1.2, 1.225, 1.25, 1.275, 1.3, 1.325, 1.35, 1.375, 1.4, 1.425, 1.45, 1.475, 1.5, 1.525, 1.55, 1.575, 1.6, 1.625, 1.65, 1.675, 1.7, 1.725, 1.75, 1.775, 1.8, 1.825, 1.85, 1.875 ), +"transitions": PoolRealArray( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +"update": 1, +"values": [ 0, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ] +} + +[sub_resource type="Animation" id=6] length = 2.66666 loop = true step = 0.0111111 @@ -91,8 +166,8 @@ tracks/4/enabled = true tracks/4/keys = { "times": PoolRealArray( 0 ), "transitions": PoolRealArray( 1 ), -"update": 0, -"values": [ Vector2( 0, -20 ) ] +"update": 1, +"values": [ Vector2( 0, -28 ) ] } tracks/5/type = "value" tracks/5/path = NodePath("Sprite:flip_h") @@ -107,83 +182,6 @@ tracks/5/keys = { "values": [ false ] } -[sub_resource type="Animation" id=6] -length = 1.28333 -loop = true -step = 0.0166667 -tracks/0/type = "value" -tracks/0/path = NodePath("Sprite:frame") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/keys = { -"times": PoolRealArray( 0, 0.0166667, 0.0333334, 0.0500001, 0.0666668, 0.0833335, 0.1, 0.116667, 0.133334, 0.15, 0.166667, 0.183334, 0.2, 0.216667, 0.233334, 0.25, 0.266667, 0.283334, 0.300001, 0.316667, 0.333334, 0.350001, 0.366667, 0.383334, 0.400001, 0.416667, 0.433334, 0.450001, 0.466668, 0.483334, 0.500001, 0.516668, 0.533334, 0.550001, 0.566668, 0.583334, 0.600001, 0.616668, 0.633335, 0.650001, 0.666668, 0.683335, 0.700001, 0.716668, 0.733335, 0.750001, 0.766668, 0.783335, 0.800002, 0.816668, 0.833335, 0.850002, 0.866668, 0.883335, 0.900002, 0.916668, 0.933335, 0.950002, 0.966669, 0.983335, 1, 1.01667, 1.03334, 1.05, 1.06667, 1.08334, 1.1, 1.11667, 1.13334, 1.15, 1.16667, 1.18334, 1.2, 1.21667, 1.23334, 1.25, 1.26667 ), -"transitions": PoolRealArray( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), -"update": 1, -"values": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 ] -} -tracks/1/type = "value" -tracks/1/path = NodePath("Sprite:texture") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 1, -"values": [ ExtResource( 6 ) ] -} -tracks/2/type = "value" -tracks/2/path = NodePath("Sprite:vframes") -tracks/2/interp = 1 -tracks/2/loop_wrap = true -tracks/2/imported = false -tracks/2/enabled = true -tracks/2/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 1, -"values": [ 7 ] -} -tracks/3/type = "value" -tracks/3/path = NodePath("Sprite:hframes") -tracks/3/interp = 1 -tracks/3/loop_wrap = true -tracks/3/imported = false -tracks/3/enabled = true -tracks/3/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 1, -"values": [ 11 ] -} -tracks/4/type = "value" -tracks/4/path = NodePath("Sprite:position") -tracks/4/interp = 1 -tracks/4/loop_wrap = true -tracks/4/imported = false -tracks/4/enabled = true -tracks/4/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 0, -"values": [ Vector2( -5, -20 ) ] -} -tracks/5/type = "value" -tracks/5/path = NodePath("Sprite:flip_h") -tracks/5/interp = 1 -tracks/5/loop_wrap = true -tracks/5/imported = false -tracks/5/enabled = true -tracks/5/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 1, -"values": [ true ] -} - [sub_resource type="Animation" id=7] length = 1.28333 loop = true @@ -245,8 +243,85 @@ tracks/4/enabled = true tracks/4/keys = { "times": PoolRealArray( 0 ), "transitions": PoolRealArray( 1 ), -"update": 0, -"values": [ Vector2( -5, -20 ) ] +"update": 1, +"values": [ Vector2( 5, -28 ) ] +} +tracks/5/type = "value" +tracks/5/path = NodePath("Sprite:flip_h") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ true ] +} + +[sub_resource type="Animation" id=8] +length = 1.28333 +loop = true +step = 0.0166667 +tracks/0/type = "value" +tracks/0/path = NodePath("Sprite:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0, 0.0166667, 0.0333334, 0.0500001, 0.0666668, 0.0833335, 0.1, 0.116667, 0.133334, 0.15, 0.166667, 0.183334, 0.2, 0.216667, 0.233334, 0.25, 0.266667, 0.283334, 0.300001, 0.316667, 0.333334, 0.350001, 0.366667, 0.383334, 0.400001, 0.416667, 0.433334, 0.450001, 0.466668, 0.483334, 0.500001, 0.516668, 0.533334, 0.550001, 0.566668, 0.583334, 0.600001, 0.616668, 0.633335, 0.650001, 0.666668, 0.683335, 0.700001, 0.716668, 0.733335, 0.750001, 0.766668, 0.783335, 0.800002, 0.816668, 0.833335, 0.850002, 0.866668, 0.883335, 0.900002, 0.916668, 0.933335, 0.950002, 0.966669, 0.983335, 1, 1.01667, 1.03334, 1.05, 1.06667, 1.08334, 1.1, 1.11667, 1.13334, 1.15, 1.16667, 1.18334, 1.2, 1.21667, 1.23334, 1.25, 1.26667 ), +"transitions": PoolRealArray( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +"update": 1, +"values": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 ] +} +tracks/1/type = "value" +tracks/1/path = NodePath("Sprite:texture") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ ExtResource( 6 ) ] +} +tracks/2/type = "value" +tracks/2/path = NodePath("Sprite:vframes") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ 7 ] +} +tracks/3/type = "value" +tracks/3/path = NodePath("Sprite:hframes") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ 11 ] +} +tracks/4/type = "value" +tracks/4/path = NodePath("Sprite:position") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ Vector2( -5, -28 ) ] } tracks/5/type = "value" tracks/5/path = NodePath("Sprite:flip_h") @@ -261,7 +336,7 @@ tracks/5/keys = { "values": [ false ] } -[sub_resource type="Animation" id=8] +[sub_resource type="Animation" id=9] length = 1.28333 loop = true step = 0.0166667 @@ -325,23 +400,84 @@ tracks/4/keys = { "update": 1, "values": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75 ] } +tracks/5/type = "value" +tracks/5/path = NodePath("Sprite:position") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 1, +"values": [ Vector2( 0, -28 ) ] +} + +[sub_resource type="AnimationNodeAnimation" id=10] +animation = "MoveRight" + +[sub_resource type="AnimationNodeAnimation" id=11] +animation = "MoveUp" + +[sub_resource type="AnimationNodeAnimation" id=12] +animation = "MoveDown" + +[sub_resource type="AnimationNodeAnimation" id=13] +animation = "MoveLeft" + +[sub_resource type="AnimationNodeBlendSpace2D" id=14] +blend_point_0/node = SubResource( 10 ) +blend_point_0/pos = Vector2( 1, 0 ) +blend_point_1/node = SubResource( 11 ) +blend_point_1/pos = Vector2( 0, -1.1 ) +blend_point_2/node = SubResource( 12 ) +blend_point_2/pos = Vector2( 0, 1.1 ) +blend_point_3/node = SubResource( 13 ) +blend_point_3/pos = Vector2( -1, 0 ) +min_space = Vector2( -1, -1.1 ) +max_space = Vector2( 1, 1.1 ) +blend_mode = 1 + +[sub_resource type="AnimationNodeStateMachine" id=15] +states/move/node = SubResource( 14 ) +states/move/position = Vector2( 143, 70 ) + +[sub_resource type="AnimationNodeStateMachinePlayback" id=16] [node name="SlimeBoss" type="KinematicBody2D"] collision_layer = 4 -collision_mask = 0 +collision_mask = 3 script = ExtResource( 4 ) __meta__ = { "_edit_group_": true } -STATES_COLLECTION = NodePath("States") -START_STATE = NodePath("States/Idle") + +[node name="Stats" parent="." instance=ExtResource( 3 )] +max_health = 3 + +[node name="States" type="Node" parent="."] + +[node name="FightStart" type="Node" parent="States"] +script = ExtResource( 9 ) + +[node name="RoamSequence" type="Node" parent="States"] +script = ExtResource( 7 ) + +[node name="Wait" type="Node" parent="States/RoamSequence"] +script = ExtResource( 8 ) + +[node name="Timer" type="Timer" parent="States/RoamSequence/Wait"] +one_shot = true + +[node name="MoveToRandomPosition" type="Node" parent="States/RoamSequence"] +script = ExtResource( 12 ) [node name="Sprite" type="Sprite" parent="."] -position = Vector2( 0, -20 ) +position = Vector2( 0, -28 ) texture = ExtResource( 5 ) vframes = 10 hframes = 24 -frame = 227 +frame = 2 [node name="Hitbox" parent="." instance=ExtResource( 2 )] visible = false @@ -380,38 +516,23 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="Stats" parent="." instance=ExtResource( 3 )] - -[node name="States" type="Node" parent="."] - -[node name="Idle" type="Node" parent="States"] -script = ExtResource( 7 ) - -[node name="Wander" type="Node" parent="States"] -script = ExtResource( 9 ) - -[node name="SplitUp" type="Node" parent="States"] -script = ExtResource( 10 ) - -[node name="Stagger" type="Node" parent="States"] -script = ExtResource( 8 ) - -[node name="JumpTeleport" type="Node" parent="States"] -script = ExtResource( 12 ) - -[node name="Dead" type="Node" parent="States"] -script = ExtResource( 13 ) - -[node name="Pursue" type="Node" parent="States"] -script = ExtResource( 14 ) - [node name="AnimationPlayer" type="AnimationPlayer" parent="."] autoplay = "MoveRight" -anims/MoveDown = SubResource( 5 ) -anims/MoveLeft = SubResource( 6 ) -anims/MoveRight = SubResource( 7 ) -anims/MoveUp = SubResource( 8 ) -[connection signal="area_entered" from="HeroCloseZone" to="States/Idle" method="hero_close"] +anims/FightStart = SubResource( 5 ) +anims/MoveDown = SubResource( 6 ) +anims/MoveLeft = SubResource( 7 ) +anims/MoveRight = SubResource( 8 ) +anims/MoveUp = SubResource( 9 ) + +[node name="AnimationTree" type="AnimationTree" parent="."] +tree_root = SubResource( 15 ) +anim_player = NodePath("../AnimationPlayer") +active = true +parameters/playback = SubResource( 16 ) +parameters/move/blend_position = Vector2( 0.210872, 0.275 ) +[connection signal="no_health" from="Stats" to="." method="_on_Stats_no_health"] +[connection signal="area_entered" from="Hurtbox" to="." method="_on_Hurtbox_area_entered"] +[connection signal="animation_finished" from="AnimationPlayer" to="." method="_on_animation_finished"] [editable path="Hitbox"] diff --git a/src/Boss/SlimeBoss/SlimeBossStateMachine.gd b/src/Boss/SlimeBoss/SlimeBossStateMachine.gd index c783ada..8339fd4 100644 --- a/src/Boss/SlimeBoss/SlimeBossStateMachine.gd +++ b/src/Boss/SlimeBoss/SlimeBossStateMachine.gd @@ -1,38 +1,181 @@ -extends "res://Overlap/StateMachine/StateMachine.gd" +extends KinematicBody2D -onready var hero_close_zone = $HeroCloseZone -onready var debug_label = $DebugLabel +onready var animation_playback = $AnimationTree.get("parameters/playback") +signal target_position_changed +signal state_changed(new_state_name) +signal phase_changed(new_phase_name) + +export(float) var MASS = 8.0 +onready var start_global_position = global_position + +var target_position = Vector2() + +var state_active = null +var sequence_cycles = 0 + +enum PHASES {PHASE_ONE, PHASE_TWO, PHASE_THREE} +export(PHASES) var _phase = PHASES.PHASE_ONE func _ready(): - states_map = { - "idle": $States/Idle, - "wander": $States/Wander, - "pursue": $States/Pursue, - "split_up": $States/SplitUp, - "stagger": $States/Stagger, - "jump_teleport": $States/JumpTeleport, - "dead": $States/Dead - } - - debug_label.text = "IDLE" + print("Hey.") + _change_phase(_phase) + $AnimationPlayer.play("__INIT__") -func change_state(state_name): - current_state.exit() - debug_label.text = state_name.to_upper() + # We check if the Player node is a sibling so that we don't get errors + # playing the WildBoar scene + if get_parent().has_node('Player'): + var player_node = get_parent().get_node('Player') + player_node.connect('position_changed', self, '_on_target_position_changed') + target_position = player_node.global_position - if state_name == "previous": - states_stack.pop_front() - elif state_name in ["stagger"]: - states_stack.push_front(states_map[state_name]) - elif state_name == "dead": - queue_free() - return + for state_node in $States.get_children(): + state_node.connect('finished', self, '_on_active_state_finished') + go_to_next_state() + + +func _physics_process(delta): + state_active.update(delta) + + +func _on_animation_finished(anim_name): + state_active._on_animation_finished(anim_name) + + +func _on_active_state_finished(): + go_to_next_state() + + +# Well.. guess I'll die. +func _on_Stats_no_health(): + print("dead") + set_invincible(true) + go_to_next_state($States/Die) + + +func go_to_next_state(state_override=null): + if state_active: + state_active.exit() + + if state_override != null: + state_active = state_override else: - var new_state = states_map[state_name] - states_stack[0] = new_state + state_active = _decide_on_next_state() + emit_signal("state_changed", state_active.name) + state_active.enter() + + +func _on_Health_health_changed(new_health): + $Tween.interpolate_property($Pivot, 'scale', Vector2(0.92, 1.12), Vector2(1.0, 1.0), 0.3, Tween.TRANS_ELASTIC, Tween.EASE_IN_OUT) + $Tween.interpolate_property($Pivot/Body, 'modulate', Color('#ff48de'), Color('#ffffff'), 0.2, Tween.TRANS_QUINT, Tween.EASE_IN) + $Tween.start() - current_state = states_stack[0] - if state_name != "previous": - # We don"t want to reinitialize the state if we"re going back to the previous state - current_state.enter() - emit_signal("state_changed", states_stack) + if _phase == PHASES.PHASE_ONE and new_health < 100: + _change_phase(PHASES.PHASE_TWO) + if _phase == PHASES.PHASE_TWO and new_health < 50: + _change_phase(PHASES.PHASE_THREE) + + +func _change_phase(new_phase): + var phase_name = "" + match new_phase: + PHASES.PHASE_ONE: + $AnimationPlayer.playback_speed = 1.0 + phase_name = "One" + PHASES.PHASE_TWO: + $AnimationPlayer.playback_speed = 1.4 + phase_name = "Two" + PHASES.PHASE_THREE: + $AnimationPlayer.playback_speed = 1.8 + phase_name = "Three" + + emit_signal("phase_changed", phase_name) + _phase = new_phase + + +# The AI's brain. This function defines the flow of states +# That's a big advantage of the state pattern +# If it grows too big you can swap it with a node +# Or move the flow of states to a data file, like JSON or a text file +func _decide_on_next_state(): + # Battle start + if state_active == null: + set_invincible(true) + return $States/FightStart + if state_active == $States/FightStart: + set_invincible(false) + return $States/RoamSequence + if state_active == $States/RoamSequence: + return $States/FightStart + +# # Death +# if state_active == $States/Die: +# queue_free() +# return $States/Dead +# +# # PHASE ONE +# if _phase == PHASES.PHASE_ONE: +# if state_active == $States/RoamSequence: +# sequence_cycles += 1 +# if sequence_cycles < 2: +# return $States/RoamSequence +# else: +# sequence_cycles = 0 +# return $States/Stomp +# if state_active == $States/Stomp: +# return $States/RoamSequence +# +# # PHASE TWO +# elif _phase == PHASES.PHASE_TWO: +# if state_active == $States/RoamSequence: +# return $States/Stomp +# if state_active == $States/Stomp: +# if sequence_cycles < 2: +# sequence_cycles += 1 +# return $States/Stomp +# else: +# sequence_cycles = 0 +# return $States/ChargeSequence +# if state_active == $States/ChargeSequence: +# return $States/RoamSequence +# +# +# # PHASE THREE +# elif _phase == PHASES.PHASE_THREE: +# if state_active == $States/RoamSequence: +# return $States/Stomp +# if state_active == $States/Stomp: +# if sequence_cycles < 2: +# sequence_cycles += 1 +# return $States/Stomp +# else: +# sequence_cycles = 0 +# return $States/ChargeSequence +# if state_active == $States/ChargeSequence: +# if sequence_cycles < 2: +# sequence_cycles += 1 +# return $States/ChargeSequence +# else: +# sequence_cycles = 0 +# return $States/Stomp + + +# Using a public method makes it so we can change the entire particle setup anytime +func set_particles_active(value): + $DustPuffsLarge.emitting = value + + +# Same thing here, we will likely need more collision shapes in the final version +# So using a function we can group these disabled toggles together +func set_invincible(value): + $CollisionShape2D.disabled = value + $Hitbox/CollisionShape2D.disabled = value + $Hurtbox/CollisionShape2D.disabled = value + + +func _on_target_position_changed(new_position): + target_position = new_position + + +func _on_Hurtbox_area_entered(area): + $Stats.health -= area.damage + diff --git a/src/Boss/SlimeBoss/States/Charge/Prepare.gd b/src/Boss/SlimeBoss/States/Charge/Prepare.gd new file mode 100644 index 0000000..d616208 --- /dev/null +++ b/src/Boss/SlimeBoss/States/Charge/Prepare.gd @@ -0,0 +1,7 @@ +extends "res://Overlap/StateMachine/State.gd" + +func enter(): + owner.get_node('AnimationPlayer').play('prepare') + +func _on_animation_finished(anim_name): + emit_signal('finished') diff --git a/src/Boss/SlimeBoss/States/FightStart.gd b/src/Boss/SlimeBoss/States/FightStart.gd new file mode 100644 index 0000000..b51aa26 --- /dev/null +++ b/src/Boss/SlimeBoss/States/FightStart.gd @@ -0,0 +1,13 @@ +extends "res://Overlap/StateMachine/State.gd" + +onready var animation_player = owner.get_node("AnimationPlayer") +onready var animation_tree = owner.get_node("AnimationTree") + +func enter(): + animation_tree.active = false + animation_player.playback_active = true + animation_player.play('FightStart') + +func _on_animation_finished(anim_name): + assert(anim_name == 'FightStart') + emit_signal('finished') diff --git a/src/Boss/SlimeBoss/States/Motion/Dead.gd b/src/Boss/SlimeBoss/States/Motion/Dead.gd deleted file mode 100644 index 576756c..0000000 --- a/src/Boss/SlimeBoss/States/Motion/Dead.gd +++ /dev/null @@ -1,4 +0,0 @@ -extends "res://Overlap/StateMachine/State.gd" - -func enter(): - print("dead state") diff --git a/src/Boss/SlimeBoss/States/Motion/Idle.gd b/src/Boss/SlimeBoss/States/Motion/Idle.gd deleted file mode 100644 index b5ee51b..0000000 --- a/src/Boss/SlimeBoss/States/Motion/Idle.gd +++ /dev/null @@ -1,7 +0,0 @@ -extends "res://Overlap/StateMachine/State.gd" - -func enter(): - owner.get_node("AnimationPlayer").play("MoveDown") - -func hero_close(area): - emit_signal("finished", "pursue") diff --git a/src/Boss/SlimeBoss/States/Motion/JumpTeleport.gd b/src/Boss/SlimeBoss/States/Motion/JumpTeleport.gd deleted file mode 100644 index 5ccf4ea..0000000 --- a/src/Boss/SlimeBoss/States/Motion/JumpTeleport.gd +++ /dev/null @@ -1,4 +0,0 @@ -extends "res://Overlap/StateMachine/State.gd" - -func enter(): - print("jump teleport state") diff --git a/src/Boss/SlimeBoss/States/Motion/Pursue.gd b/src/Boss/SlimeBoss/States/Motion/Pursue.gd deleted file mode 100644 index 9d604c4..0000000 --- a/src/Boss/SlimeBoss/States/Motion/Pursue.gd +++ /dev/null @@ -1,4 +0,0 @@ -extends "res://Overlap/StateMachine/State.gd" - -func enter(): - print("pursue state") diff --git a/src/Boss/SlimeBoss/States/Motion/SplitUp.gd b/src/Boss/SlimeBoss/States/Motion/SplitUp.gd deleted file mode 100644 index 845fd18..0000000 --- a/src/Boss/SlimeBoss/States/Motion/SplitUp.gd +++ /dev/null @@ -1,4 +0,0 @@ -extends "res://Overlap/StateMachine/State.gd" - -func enter(): - print("split up state") diff --git a/src/Boss/SlimeBoss/States/Motion/Stagger.gd b/src/Boss/SlimeBoss/States/Motion/Stagger.gd deleted file mode 100644 index 05d8da0..0000000 --- a/src/Boss/SlimeBoss/States/Motion/Stagger.gd +++ /dev/null @@ -1,4 +0,0 @@ -extends "res://Overlap/StateMachine/State.gd" - -func enter(): - print("stagger state") diff --git a/src/Boss/SlimeBoss/States/Motion/Wander.gd b/src/Boss/SlimeBoss/States/Motion/Wander.gd deleted file mode 100644 index c0c535b..0000000 --- a/src/Boss/SlimeBoss/States/Motion/Wander.gd +++ /dev/null @@ -1,4 +0,0 @@ -extends "res://Overlap/StateMachine/State.gd" - -func enter(): - print("wander state") diff --git a/src/Boss/SlimeBoss/States/Roam/MoveToRandomPosition.gd b/src/Boss/SlimeBoss/States/Roam/MoveToRandomPosition.gd new file mode 100644 index 0000000..16908d7 --- /dev/null +++ b/src/Boss/SlimeBoss/States/Roam/MoveToRandomPosition.gd @@ -0,0 +1,34 @@ +extends "res://Overlap/StateMachine/State.gd" + +export(float) var ARRIVE_DISTANCE = 6.0 +export(float) var SLOW_RADIUS = 200.0 +export(float) var MASS = 4.0 +export(float) var MAX_SPEED = 300.0 +export(float) var ROAM_RADIUS = 150.0 + + + +var target_position = Vector2() +var start_position = Vector2() +var velocity = Vector2() + +func enter(): + start_position = get_parent().start_position + target_position = calculate_new_target_position() + owner.get_node('AnimationPlayer').play('move') + + +func update(delta): + velocity = Steering.arrive_to(velocity, owner.global_position, target_position, MASS, SLOW_RADIUS, MAX_SPEED) + owner.move_and_slide(velocity) + + if owner.global_position.distance_to(target_position) < ARRIVE_DISTANCE: + emit_signal('finished') + + +func calculate_new_target_position(): + randomize() + var random_angle = randf() * 2 * PI + randomize() + var random_radius = (randf() * ROAM_RADIUS) / 2 + ROAM_RADIUS / 2 + return start_position + Vector2(cos(random_angle) * random_radius, sin(random_angle) * random_radius) diff --git a/src/Boss/SlimeBoss/States/Roam/RoamSequence.gd b/src/Boss/SlimeBoss/States/Roam/RoamSequence.gd new file mode 100644 index 0000000..3b4087f --- /dev/null +++ b/src/Boss/SlimeBoss/States/Roam/RoamSequence.gd @@ -0,0 +1,15 @@ +extends "res://Overlap/StateMachine/SequenceState.gd" + +var start_position = Vector2() + +func enter(): + start_position = owner.global_position + .enter() + + +func exit(): + .exit() + + +func update(delta): + .update(delta) diff --git a/src/Boss/SlimeBoss/States/Roam/Wait.gd b/src/Boss/SlimeBoss/States/Roam/Wait.gd new file mode 100644 index 0000000..dfc5be8 --- /dev/null +++ b/src/Boss/SlimeBoss/States/Roam/Wait.gd @@ -0,0 +1,12 @@ +extends "res://Overlap/StateMachine/State.gd" + +func enter(): + owner.get_node('AnimationPlayer').play('idle') + $Timer.start() + +func update(delta): + if $Timer.time_left <= 0.0: + emit_signal('finished') + +func exit(): + $Timer.stop() diff --git a/src/Debug/BossStateDisplay.gd b/src/Debug/BossStateDisplay.gd new file mode 100644 index 0000000..41d8ed8 --- /dev/null +++ b/src/Debug/BossStateDisplay.gd @@ -0,0 +1,8 @@ +extends Panel + +func _on_SlimeBoss_state_changed(new_state_name): + $VBoxContainer/State.text = new_state_name + + +func _on_SlimeBoss_phase_changed(new_phase_name): + $VBoxContainer/Phase.text = "Phase: " + new_phase_name diff --git a/src/Overlap/StateMachine/SequenceState.gd b/src/Overlap/StateMachine/SequenceState.gd new file mode 100644 index 0000000..e9e68e7 --- /dev/null +++ b/src/Overlap/StateMachine/SequenceState.gd @@ -0,0 +1,41 @@ +extends 'State.gd' + +var state_active = null + + +func _ready(): + for child in get_children(): + child.connect('finished', self, '_on_state_active_finished') + + +func enter(): + state_active = get_child(0) + state_active.enter() + + +func exit(): + state_active = null + + +func update(delta): + state_active.update(delta) + + +func _on_animation_finished(anim_name): + state_active._on_animation_finished(anim_name) + + +func _on_state_active_finished(): + go_to_next_state_in_sequence() + + +func go_to_next_state_in_sequence(): + state_active.exit() + + var new_state_index = (state_active.get_index() + 1) % get_child_count() + if new_state_index == 0: + emit_signal('finished') + return + state_active = get_child(new_state_index) + + state_active.enter() diff --git a/src/Player/Player.tscn b/src/Player/Player.tscn index beecd07..553af83 100644 --- a/src/Player/Player.tscn +++ b/src/Player/Player.tscn @@ -626,7 +626,7 @@ font_data = ExtResource( 6 ) [node name="Player" type="KinematicBody2D" groups=[ "hero", ]] -collision_mask = 10 +collision_mask = 14 script = ExtResource( 1 ) FRICTION = 270 diff --git a/src/World.tscn b/src/World.tscn index fbcf33a..17e6dcd 100644 --- a/src/World.tscn +++ b/src/World.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=2] +[gd_scene load_steps=13 format=2] [ext_resource path="res://Player/Player.tscn" type="PackedScene" id=1] [ext_resource path="res://World.gd" type="Script" id=2] @@ -8,7 +8,9 @@ [ext_resource path="res://Menus/DialogueBox/DialogueBox.tscn" type="PackedScene" id=6] [ext_resource path="res://Maps/Background/Background.tscn" type="PackedScene" id=7] [ext_resource path="res://Boss/SlimeBoss/SlimeBoss.tscn" type="PackedScene" id=8] +[ext_resource path="res://Debug/BossStateDisplay.gd" type="Script" id=9] [ext_resource path="res://Objects/Bonfire/Bonfire.tscn" type="PackedScene" id=10] +[ext_resource path="res://Fonts/Harmonic/Harmonic12.tres" type="DynamicFont" id=11] [ext_resource path="res://Maps/Grid.tscn" type="PackedScene" id=18] [node name="World" type="Node2D"] @@ -21,10 +23,10 @@ region_enabled = true region_rect = Rect2( 0, 0, 1280, 720 ) [node name="Background" parent="." instance=ExtResource( 7 )] -frame = 24 +frame = 49 [node name="FloorTileMap" type="TileMap" parent="."] -modulate = Color( 1, 1, 1, 0.454902 ) +visible = false position = Vector2( 16, 16 ) tile_set = ExtResource( 3 ) cell_size = Vector2( 32, 32 ) @@ -38,18 +40,20 @@ __meta__ = { } [node name="YSort" type="YSort" parent="."] -position = Vector2( 152, 120 ) [node name="Bonfire" parent="YSort" instance=ExtResource( 10 )] position = Vector2( 265.543, -16 ) [node name="Player" parent="YSort" instance=ExtResource( 1 )] -position = Vector2( 168, -12.7226 ) +position = Vector2( 344, 125.768 ) scale = Vector2( 2, 2 ) debug = true ROLL_SPEED = 140 FRICTION = 200 +[node name="SlimeBoss" parent="YSort" instance=ExtResource( 8 )] +position = Vector2( 104, 80 ) + [node name="Grid" parent="." instance=ExtResource( 18 )] [node name="CanvasLayer" type="CanvasLayer" parent="."] @@ -60,5 +64,46 @@ visible = false [node name="DragNDropUI" parent="CanvasLayer" instance=ExtResource( 5 )] ObjectParent = NodePath("../..") -[node name="SlimeBoss" parent="." instance=ExtResource( 8 )] -position = Vector2( 88, 75.8131 ) +[node name="DebugInterface" type="Control" parent="CanvasLayer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="BossStateDisplay" type="Panel" parent="CanvasLayer/DebugInterface"] +anchor_left = 1.0 +anchor_right = 1.0 +margin_left = -80.0 +margin_bottom = 60.0 +script = ExtResource( 9 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/DebugInterface/BossStateDisplay"] +margin_right = 40.0 +margin_bottom = 40.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Title" type="Label" parent="CanvasLayer/DebugInterface/BossStateDisplay/VBoxContainer"] +margin_right = 40.0 +margin_bottom = 12.0 +custom_fonts/font = ExtResource( 11 ) +text = "Boss" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Phase" type="Label" parent="CanvasLayer/DebugInterface/BossStateDisplay/VBoxContainer"] +margin_top = 16.0 +margin_right = 40.0 +margin_bottom = 28.0 +custom_fonts/font = ExtResource( 11 ) + +[node name="State" type="Label" parent="CanvasLayer/DebugInterface/BossStateDisplay/VBoxContainer"] +margin_top = 32.0 +margin_right = 40.0 +margin_bottom = 44.0 +custom_fonts/font = ExtResource( 11 ) +[connection signal="phase_changed" from="YSort/SlimeBoss" to="CanvasLayer/DebugInterface/BossStateDisplay" method="_on_SlimeBoss_phase_changed"] +[connection signal="state_changed" from="YSort/SlimeBoss" to="CanvasLayer/DebugInterface/BossStateDisplay" method="_on_SlimeBoss_state_changed"] diff --git a/src/project.godot b/src/project.godot index 232909c..4492c03 100644 --- a/src/project.godot +++ b/src/project.godot @@ -57,6 +57,7 @@ config/icon="res://icon.png" [autoload] SoundControler="*res://Autoloads/SoundControler.gd" +Steering="*res://Autoloads/Steering.gd" [display]