diff --git a/core/demotivation/bubble.gd b/core/demotivation/bubble.gd new file mode 100644 index 0000000..1a6becb --- /dev/null +++ b/core/demotivation/bubble.gd @@ -0,0 +1,46 @@ +## demotivation bubble which was produced by a grunt. +@tool +extends Node2D + + +signal player_hit + + +@export var text = "": + set(t): + text = t + _set_text.call_deferred(text) +@export var velocity:Vector2 = Vector2.ZERO + + +@onready var label: Label = $Label +@onready var text_collision: CollisionShape2D = %TextCollision +@onready var area_2d: Area2D = $Area2D +@onready var visible_on_screen_notifier_2d: VisibleOnScreenNotifier2D = $VisibleOnScreenNotifier2D + + +func _ready() -> void: + _set_text(text) + area_2d.area_entered.connect(_player_hit) + visible_on_screen_notifier_2d.screen_exited.connect(_despawn) + + +func _process(delta: float) -> void: + position += velocity * delta + + +func _player_hit(area) -> void: + player_hit.emit() + area_2d.set_collision_mask_value(4, false) + _despawn() + + +func _set_text(t:String) -> void: + label.text = t + var shape = text_collision.shape as RectangleShape2D + shape.size.x = t.length() * 4 + + +func _despawn() -> void: + # TODO: animate out properly! + queue_free() diff --git a/core/demotivation/bubble.gd.uid b/core/demotivation/bubble.gd.uid new file mode 100644 index 0000000..d962ba0 --- /dev/null +++ b/core/demotivation/bubble.gd.uid @@ -0,0 +1 @@ +uid://dy48rxt4dxpmu diff --git a/core/demotivation/bubble.tscn b/core/demotivation/bubble.tscn new file mode 100644 index 0000000..212c893 --- /dev/null +++ b/core/demotivation/bubble.tscn @@ -0,0 +1,34 @@ +[gd_scene format=3 uid="uid://juvwef0aqnxy"] + +[ext_resource type="Script" uid="uid://dy48rxt4dxpmu" path="res://core/demotivation/bubble.gd" id="1_4y7xw"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_lwh48"] +resource_local_to_scene = true +size = Vector2(0, 15) + +[node name="Bubble" type="Node2D" unique_id=1424086739] +z_index = 1 +script = ExtResource("1_4y7xw") +velocity = null + +[node name="Label" type="Label" parent="." unique_id=1201313009] +custom_minimum_size = Vector2(200, 0) +offset_left = -99.0 +offset_top = -9.0 +offset_right = 101.0 +offset_bottom = 7.0 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Area2D" type="Area2D" parent="." unique_id=1983305449] +collision_layer = 0 +collision_mask = 8 + +[node name="TextCollision" type="CollisionShape2D" parent="Area2D" unique_id=468309209] +unique_name_in_owner = true +position = Vector2(0, 0.5) +shape = SubResource("RectangleShape2D_lwh48") + +[node name="VisibleOnScreenNotifier2D" type="VisibleOnScreenNotifier2D" parent="." unique_id=1389283880] +position = Vector2(-6, 3) +rect = Rect2(-10, -10, 32, 15) diff --git a/core/demotivation/demotivator.gd b/core/demotivation/demotivator.gd new file mode 100644 index 0000000..e1d0796 --- /dev/null +++ b/core/demotivation/demotivator.gd @@ -0,0 +1,62 @@ +extends Node2D + + +const DEMOTIVATIONS = [ + "Go home", + "You suck", + "Let's relax instead", + "Leg day is annoying", + "Gym is for posers", + "It's too warm to train", + "Who needs fitness anyways", + "Let's go tomorrow instead", + "Have you seen this reel?", + "It doesn't do any good anyway" +] + +const BUBBLE_SPEED_MIN = 25 +const BUBBLE_SPEED_MAX = 50 +const DEMOTIVATION_PER_BUBBLE = 25 +const MIN_SECONDS_SINCE_LAST_ATTACK = 1.5 +const MIN_DISTANCE_TO_DEMOTIVATE = 50 + +const BubbleScene = preload("res://core/demotivation/bubble.tscn") + +@export var player_stats:PlayerStats +@export var player:Character + + +@onready var timer: Timer = $Timer + + +func _ready() -> void: + randomize() + timer.timeout.connect(_demotivate) + + +func _demotivate() -> void: + var brutes = get_tree().get_nodes_in_group("demotivating")\ + ## only let brutes demotivate that are actually awake and on screen + .filter(func(brute:Character): return brute.awake() and brute.is_on_screen())\ + ## when punching a brute they should not be able to demotivate you + .filter(func(brute:Character): return brute.seconds_since_last_hit() > MIN_SECONDS_SINCE_LAST_ATTACK)\ + ## when approaching a brute, it feels unfair when they demotivate and instantly hit you + .filter(func(brute:Character): + var distance = (player.global_position - brute.global_position).length() + return distance >= MIN_DISTANCE_TO_DEMOTIVATE + ) + if brutes.is_empty(): + return + var brute = brutes.pick_random() as Character + var bubble = BubbleScene.instantiate() + bubble.global_position = brute.global_position + bubble.text = DEMOTIVATIONS.pick_random() + add_child(bubble) + + var direction = (player.global_position - brute.global_position).normalized() + var speed = randf_range(BUBBLE_SPEED_MIN, BUBBLE_SPEED_MAX) + bubble.velocity = direction * speed + bubble.player_hit.connect(func(): + player_stats.motivation -= DEMOTIVATION_PER_BUBBLE + ) + diff --git a/core/demotivation/demotivator.gd.uid b/core/demotivation/demotivator.gd.uid new file mode 100644 index 0000000..471edaf --- /dev/null +++ b/core/demotivation/demotivator.gd.uid @@ -0,0 +1 @@ +uid://bxlch7pmxerk3 diff --git a/core/demotivation/demotivator.tscn b/core/demotivation/demotivator.tscn new file mode 100644 index 0000000..a326dd4 --- /dev/null +++ b/core/demotivation/demotivator.tscn @@ -0,0 +1,10 @@ +[gd_scene format=3 uid="uid://cpygw4dna4bpm"] + +[ext_resource type="Script" uid="uid://bxlch7pmxerk3" path="res://core/demotivation/demotivator.gd" id="1_6ej1k"] + +[node name="Demotivator" type="Node2D" unique_id=267041975] +script = ExtResource("1_6ej1k") + +[node name="Timer" type="Timer" parent="." unique_id=137173420] +wait_time = 6.0 +autostart = true diff --git a/core/player_stats.gd b/core/player_stats.gd index b9d72e8..bd315ec 100644 --- a/core/player_stats.gd +++ b/core/player_stats.gd @@ -7,7 +7,7 @@ signal score_changed(new_score:int) signal unmotivated -@export var motivation_drain_factor = 5 +@export var motivation_drain_factor = 1 @onready var timer: Timer = $Timer diff --git a/core/player_stats.tscn b/core/player_stats.tscn index 898b0ed..29f215a 100644 --- a/core/player_stats.tscn +++ b/core/player_stats.tscn @@ -4,7 +4,7 @@ [node name="PlayerStats" type="Node" unique_id=1041102396] script = ExtResource("1_8jqq8") +motivation_drain_factor = 5 [node name="Timer" type="Timer" parent="." unique_id=1090392545] -wait_time = 2.0 autostart = true diff --git a/entities/brute.tscn b/entities/brute.tscn index 9028d3b..6c70973 100644 --- a/entities/brute.tscn +++ b/entities/brute.tscn @@ -166,7 +166,7 @@ radius = 12.0 radius = 18.0 height = 44.0 -[node name="Brute" unique_id=822027437 instance=ExtResource("1_na6nm")] +[node name="Brute" unique_id=822027437 groups=["demotivating"] instance=ExtResource("1_na6nm")] script = ExtResource("2_hug0n") speed = 70.0 ai_script = ExtResource("3_rc22r") @@ -197,4 +197,7 @@ horizontal_alignment = 1 position = Vector2(0, -15) shape = SubResource("CapsuleShape2D_lloi5") +[node name="VisibleOnScreenNotifier2D" parent="." index="6"] +rect = Rect2(-20, -39, 40, 48) + [editable path="HitBox"] diff --git a/entities/character.gd b/entities/character.gd index d5f9dea..6af81fe 100644 --- a/entities/character.gd +++ b/entities/character.gd @@ -20,6 +20,7 @@ var current_impulse:Vector2 = Vector2.ZERO var current_impulse_z = 0 var ai:CharacterAi var direction:Vector2 = Vector2.RIGHT +var latest_hit_time = -1 func _ready() -> void: if ai_script: @@ -55,10 +56,16 @@ func _ready() -> void: hit_box.damaged.connect(func(dmg): if not hit_box.ko(): sprite_2d.play("hit") + latest_hit_time = Time.get_ticks_msec() hit()) hit_box.death.connect(func(): sprite_2d.play("ko") ) + + +func seconds_since_last_hit() -> float: + return (Time.get_ticks_msec() - latest_hit_time) / 1000.0 + func walk_left() -> void: motion.x -= speed @@ -75,6 +82,10 @@ func walk_up() -> void: func walk_down() -> void: motion.y += speed + + +func awake() -> bool: + return not hit_box.ko() func impulse(impulse:Vector3) -> void: diff --git a/entities/entity.gd b/entities/entity.gd index 4bf8d45..add163f 100644 --- a/entities/entity.gd +++ b/entities/entity.gd @@ -21,6 +21,7 @@ const MAX_HEIGHT = 100 @onready var shadow: AnimatedSprite2D = $Shadow @onready var sprite_2d: AnimatedSprite2D = $Sprite2D +@onready var visible_on_screen_notifier_2d: VisibleOnScreenNotifier2D = $VisibleOnScreenNotifier2D func hit() -> void: @@ -31,5 +32,9 @@ func hit() -> void: hit_tween.tween_property(mat, "shader_parameter/blend_delta", 0.0, 0.2) +func is_on_screen() -> bool: + return visible_on_screen_notifier_2d.is_on_screen() + + func remap_range(input:int, minInput:int, maxInput:int, minOutput:int, maxOutput:int) -> int: return floori(float(input - minInput) / float(maxInput - minInput) * float(maxOutput - minOutput) + minOutput) diff --git a/entities/entity.tscn b/entities/entity.tscn index d833947..68d4c4d 100644 --- a/entities/entity.tscn +++ b/entities/entity.tscn @@ -72,3 +72,5 @@ sprite_frames = SubResource("SpriteFrames_rfe5m") [node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=490700924] visible = false + +[node name="VisibleOnScreenNotifier2D" type="VisibleOnScreenNotifier2D" parent="." unique_id=225490077] diff --git a/entities/player.tscn b/entities/player.tscn index 4a63c37..f43a4ed 100644 --- a/entities/player.tscn +++ b/entities/player.tscn @@ -99,6 +99,9 @@ height = 14.0 [sub_resource type="CircleShape2D" id="CircleShape2D_lwh48"] radius = 10.049875 +[sub_resource type="CircleShape2D" id="CircleShape2D_d3hfy"] +radius = 18.027756 + [node name="Player" unique_id=822027437 groups=["player"] instance=ExtResource("1_merdl")] collision_layer = 9 motion_mode = 0 @@ -129,3 +132,11 @@ position = Vector2(14, -8) shape = SubResource("CircleShape2D_lwh48") [node name="ComboTimer" type="Timer" parent="." index="5" unique_id=1721738349] + +[node name="InteractionCollision" type="Area2D" parent="." index="8" unique_id=1200521653] +position = Vector2(0, -12) +collision_layer = 8 +collision_mask = 0 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="InteractionCollision" index="0" unique_id=492385258] +shape = SubResource("CircleShape2D_d3hfy") diff --git a/project.godot b/project.godot index 0e2050e..3a857f2 100644 --- a/project.godot +++ b/project.godot @@ -47,6 +47,7 @@ folder_colors={ camera="" player="" +demotivating="Entities of this group will demotivate the player." [gui] diff --git a/scenes/gameplay_scene.tscn b/scenes/gameplay_scene.tscn index d84723c..a1abd95 100644 --- a/scenes/gameplay_scene.tscn +++ b/scenes/gameplay_scene.tscn @@ -3,13 +3,13 @@ [ext_resource type="Script" uid="uid://cp12br43at826" path="res://scenes/gameplay_scene.gd" id="1_01stm"] [ext_resource type="PackedScene" uid="uid://coqnxmbcr13gp" path="res://entities/player.tscn" id="2_b2a1o"] [ext_resource type="PackedScene" uid="uid://2dc5gjo4wghp" path="res://core/player_stats.tscn" id="2_mvhas"] +[ext_resource type="PackedScene" uid="uid://cpygw4dna4bpm" path="res://core/demotivation/demotivator.tscn" id="3_27dq3"] [ext_resource type="PackedScene" uid="uid://dt8np820s5v1q" path="res://ui/motivation_bar.tscn" id="4_27dq3"] [node name="GameplayScene" type="Node2D" unique_id=1216125844] script = ExtResource("1_01stm") [node name="PlayerStats" parent="." unique_id=1041102396 instance=ExtResource("2_mvhas")] -motivation_drain_factor = 3 [node name="BottomCollision" type="StaticBody2D" parent="." unique_id=326297159] position = Vector2(5, 0) @@ -57,6 +57,10 @@ unique_name_in_owner = true [node name="Entities" type="Node2D" parent="." unique_id=1550402524] y_sort_enabled = true +[node name="Demotivator" parent="Entities" unique_id=267041975 node_paths=PackedStringArray("player_stats", "player") instance=ExtResource("3_27dq3")] +player_stats = NodePath("../../PlayerStats") +player = NodePath("../Player") + [node name="Player" parent="Entities" unique_id=1795706325 node_paths=PackedStringArray("player_stats") instance=ExtResource("2_b2a1o")] unique_name_in_owner = true position = Vector2(5, 17)