diff --git a/entities/Flowers.tscn b/entities/Flowers.tscn index cd4577c..7105edf 100644 --- a/entities/Flowers.tscn +++ b/entities/Flowers.tscn @@ -126,3 +126,13 @@ libraries = { autoplay = "Highlight" [node name="Snail" parent="." instance=ExtResource("5_5uu7l")] + +[node name="HealthBar" type="ProgressBar" parent="."] +offset_left = -50.0 +offset_top = 90.0 +offset_right = 50.0 +offset_bottom = 110.0 +mouse_filter = 2 +max_value = 10.0 +step = 1.0 +show_percentage = false diff --git a/entities/Snail.tscn b/entities/Snail.tscn index a6b64ef..3c65461 100644 --- a/entities/Snail.tscn +++ b/entities/Snail.tscn @@ -1,24 +1,33 @@ -[gd_scene load_steps=5 format=3 uid="uid://bnwvtlsvxjmel"] +[gd_scene load_steps=6 format=3 uid="uid://bnwvtlsvxjmel"] [ext_resource type="Script" path="res://entities/scripts/snail.gd" id="1_lkvd1"] [ext_resource type="Script" path="res://entities/scripts/finite_state_machine.gd" id="1_tejvt"] [ext_resource type="Script" path="res://entities/snail/states/snail_sleeping.gd" id="3_wnrnl"] [ext_resource type="Script" path="res://entities/snail/states/snail_eating.gd" id="4_1abwi"] -[node name="Snail" type="Sprite2D"] +[sub_resource type="CircleShape2D" id="CircleShape2D_2whjo"] +radius = 42.0476 + +[node name="Snail" type="CharacterBody2D"] +collision_layer = 8 +collision_mask = 8 +input_pickable = true script = ExtResource("1_lkvd1") [node name="AnimationPlayer" type="AnimationPlayer" parent="."] [node name="Polygon2D" type="Polygon2D" parent="."] -polygon = PackedVector2Array(-8, -8, 4, -11, 10, 5, -7, 9, -25, 8, -22, 0, -11, 0) +polygon = PackedVector2Array(-8, -8, -5, -1, 10, 5, -7, 9, -25, 8, -28, -5, -20, -11) [node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("initial_state")] script = ExtResource("1_tejvt") -initial_state = NodePath("Sleeping") +initial_state = NodePath("Eating") [node name="Sleeping" type="Node" parent="StateMachine"] script = ExtResource("3_wnrnl") [node name="Eating" type="Node" parent="StateMachine"] script = ExtResource("4_1abwi") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_2whjo") diff --git a/entities/scripts/director_drone.gd b/entities/scripts/director_drone.gd index 6223295..ddf0ec8 100644 --- a/entities/scripts/director_drone.gd +++ b/entities/scripts/director_drone.gd @@ -1,6 +1,6 @@ class_name DirectorDrone extends Drone -@onready var edit_cursor : Resource = preload("res://resources/cursors/message_dots_round.png") + @onready var label : Label = get_node("Label") @export var visit_order : int = 0 : @@ -17,21 +17,19 @@ func _on_click_detection_mouse_entered() -> void: if GameState.placing_drone == false: Log.pr("Mouse entered the director drone!") label.visible = true - Input.set_custom_mouse_cursor(edit_cursor, Input.CURSOR_ARROW, Vector2(32, 32)) + CursorMgr.edit() func _on_click_detection_mouse_exited() -> void: if GameState.placing_drone == false: Log.pr("Mouse exited the director drone!") label.visible = false - #Input.set_custom_mouse_cursor(null) - GameState.reset_cursor() + CursorMgr.reset_cursor() func _on_click_detection_input_event(_viewport:Node, event:InputEvent, _shape_idx:int) -> void: if GameState.placing_drone == false: if (event is InputEventMouseButton && event.button_index == MOUSE_BUTTON_RIGHT && event.pressed): - #Input.set_custom_mouse_cursor(null) - GameState.reset_cursor() + CursorMgr.reset_cursor() queue_free() get_parent().get_parent().update_director_drone_list() diff --git a/entities/scripts/finite_state_machine.gd b/entities/scripts/finite_state_machine.gd index 1146617..fcfb088 100644 --- a/entities/scripts/finite_state_machine.gd +++ b/entities/scripts/finite_state_machine.gd @@ -28,6 +28,12 @@ func _physics_process(delta : float) -> void: if current_state: current_state.physics_update(delta) +func get_current_state_name() -> String: + if current_state: + return current_state.name.to_lower() + else: + return "" + # Use force_change_state cautiously, it immediately switches to a state regardless of any transitions. # This is used to force us into a 'death state' when killed func force_change_state(new_state : String) -> void: diff --git a/entities/scripts/flowers.gd b/entities/scripts/flowers.gd index 357817d..5d11048 100644 --- a/entities/scripts/flowers.gd +++ b/entities/scripts/flowers.gd @@ -10,17 +10,25 @@ var spawn_snails : bool = false @onready var spawn_area : CollisionShape2D = $FlowerCollectionArea/CollisionShape2D @onready var snail : Snail = $Snail +var last_health_check : float = 0 +var health_check_timer : float = 0.5 + +@onready var health_bar : ProgressBar = $HealthBar + func _ready() -> void: hide_outline() + setup_healthbar() ## Check if this level is spawning snails or not if GameState.spawn_snails: Log.pr("Going to be spawning snails!") spawn_snails = true - Log.pr(get_random_snail_spawn()) - snail.global_position = get_random_snail_spawn() + Log.pr(get_random_circumference_points()) + snail.global_position = get_random_circumference_points() +func _process(delta: float) -> void: + update_healthbar(delta) func show_outline() -> void: outline.visible = true @@ -29,7 +37,7 @@ func hide_outline() -> void: outline.visible = false -func get_random_snail_spawn() -> Vector2: +func get_random_circumference_points() -> Vector2: var circle_radius : float = spawn_area.shape.radius var random_angle : float = randf_range(0, TAU) @@ -39,4 +47,24 @@ func get_random_snail_spawn() -> Vector2: var circle_center : Vector2 = spawn_area.global_position var random_point : Vector2 = circle_center + Vector2(x, y) - return random_point \ No newline at end of file + return random_point + +## Initial setup of the health bar - updating the values +# and hiding it from view. +func setup_healthbar() -> void: + health_bar.max_value = GameState.max_flower_nectar_level + health_bar.value = GameState.flower_nectar_level + health_bar.visible = false + +## Update the health bar based on the current GameState values +# and display it if required. Conversely, hide it if the flower is at max powah. +func update_healthbar(delta : float) -> void: + last_health_check += delta + if last_health_check >= health_check_timer: + last_health_check = 0 + if GameState.flower_nectar_level < GameState.max_flower_nectar_level: + health_bar.value = GameState.flower_nectar_level + health_bar.visible = true + else: + health_bar.value = GameState.max_flower_nectar_level + health_bar.visible = false \ No newline at end of file diff --git a/entities/scripts/snail.gd b/entities/scripts/snail.gd index df73ce8..0bb18a6 100644 --- a/entities/scripts/snail.gd +++ b/entities/scripts/snail.gd @@ -1,6 +1,46 @@ -extends Sprite2D +extends CharacterBody2D class_name Snail +@onready var fsm : FiniteStateMachine = $StateMachine as FiniteStateMachine +@onready var flowers : Flowers = get_parent() + var enabled : bool = false var eating : bool = false +var speed : float = 20.0 +var mouse_over : bool = false +func _ready() -> void: + connect("mouse_entered", Callable(self, "on_mouse_entered")) + connect("mouse_exited", Callable(self, "on_mouse_exited")) + +# Detect mouse left click and trigger function +func _input(event : InputEvent) -> void: + if mouse_over and eating: + if event is InputEventMouseButton: + if (event is InputEventMouseButton && event.button_index == MOUSE_BUTTON_LEFT && event.pressed): + maybe_sleep() + +func eat() -> void: + # Play the munch animation and noise + + # Reduce the GameState flower_nectar_level + GameState.flower_nectar_level -= 1 + +func maybe_sleep() -> void: + # If the snail is still eating, then we want a 30% chance of switching it it to the sleeping state + if eating: + if randf() < 0.3: + if fsm.get_current_state_name() == "eating": + fsm.change_state(fsm.current_state, "sleeping") + +func get_random_target() -> Vector2: + return flowers.get_random_circumference_points() + +func on_mouse_entered() -> void: + mouse_over = true + Log.pr("Mouse entered the snail!") + +func on_mouse_exited() -> void: + # Reset the cursor to the default + mouse_over = false + CursorMgr.reset_cursor() \ No newline at end of file diff --git a/entities/snail/states/snail_eating.gd b/entities/snail/states/snail_eating.gd index 3212775..de749f6 100644 --- a/entities/snail/states/snail_eating.gd +++ b/entities/snail/states/snail_eating.gd @@ -3,16 +3,36 @@ class_name SnailEating @onready var snail : Snail = get_parent().get_parent() as Snail # I think this is bad but I dont care it works +var eat_interval : float = 3.0 +var eat_timer : float = 0.0 + +var move_to : Vector2 = Vector2.ZERO + func enter(_msg : Dictionary = {}) -> void: - Log.pr("I am a snail...") + Log.pr("I am a snail and I will eat!") snail.eating = true func exit() -> void: snail.eating = false -func update(_delta : float) -> void: - pass +func update(delta : float) -> void: + eat_timer += delta + + if eat_timer >= eat_interval: + snail.eat() + eat_timer = 0.0 func physics_update(_delta : float) -> void: - pass + + if move_to == Vector2.ZERO: + move_to = snail.get_random_target() + + if snail.global_position.distance_to(move_to) > 3: + snail.velocity = snail.global_position.direction_to(move_to) * snail.speed + snail.move_and_slide() + snail.look_at(move_to) + else: + move_to = Vector2.ZERO + + diff --git a/levels/level_2.tscn b/levels/level_2.tscn index c0cf5ba..fcce4e1 100644 --- a/levels/level_2.tscn +++ b/levels/level_2.tscn @@ -144,6 +144,7 @@ unique_name_in_owner = true z_index = 900 offset_right = 1280.0 offset_bottom = 720.0 +mouse_filter = 1 [node name="DroneManager" parent="." instance=ExtResource("13_pibpn")] unique_name_in_owner = true diff --git a/project.godot b/project.godot index a71576a..0f70055 100644 --- a/project.godot +++ b/project.godot @@ -19,8 +19,9 @@ config/windows_native_icon="res://resources/icons/icon.ico" [autoload] -GameState="*res://utility/game_state.gd" Str="*res://utility/utility_strings.gd" +CursorMgr="*res://utility/cursor_manager.gd" +GameState="*res://utility/game_state.gd" SceneMgr="*res://utility/global_scene_manager.gd" HighScoreMgr="*res://utility/high_scores.gd" diff --git a/scenes/scripts/drone_manager.gd b/scenes/scripts/drone_manager.gd index 03e3a27..2b9c96f 100644 --- a/scenes/scripts/drone_manager.gd +++ b/scenes/scripts/drone_manager.gd @@ -13,7 +13,6 @@ var director_drones : Array = [] # List of all director drones in the world @onready var drone_controls : HBoxContainer = %DroneControls @onready var ui_controls : UIComponent = get_parent().get_node("UiComponent") @onready var spawned_drones_container : Node = get_node("SpawnedDrones") -@onready var place_cursor : Resource = preload("res://resources/cursors/target_round_b.png") # Drones! @onready var director_drone : Resource = preload("res://entities/DirectorDrone.tscn") @@ -83,7 +82,7 @@ func spawn_drone(drone_type : String) -> void: func place_drone(drone_type : String) -> void: if !spawning_drone: - Input.set_custom_mouse_cursor(place_cursor, Input.CURSOR_ARROW, Vector2(32, 32)) + CursorMgr.place() drone_controls.disable_buttons() Log.pr("Placing " + drone_type + "...") spawning_drone = true @@ -91,8 +90,7 @@ func place_drone(drone_type : String) -> void: spawning_type = drone_type func cancel_spawning() -> void: - #Input.set_custom_mouse_cursor(null) - GameState.reset_cursor() + CursorMgr.reset_cursor() drone_controls.reset_button_focus() drone_controls.enable_buttons() spawning_drone = false diff --git a/utility/cursor_manager.gd b/utility/cursor_manager.gd new file mode 100644 index 0000000..9a5bb36 --- /dev/null +++ b/utility/cursor_manager.gd @@ -0,0 +1,15 @@ +extends Node +class_name CursorManager + +@onready var default_cursor : Resource = preload("res://resources/cursors/pointer_a.png") +@onready var place_cursor : Resource = preload("res://resources/cursors/target_round_b.png") +@onready var edit_cursor : Resource = preload("res://resources/cursors/message_dots_round.png") + +func reset_cursor() -> void: + Input.set_custom_mouse_cursor(default_cursor, Input.CURSOR_ARROW, Vector2(8, 8)) + +func place() -> void: + Input.set_custom_mouse_cursor(place_cursor, Input.CURSOR_ARROW, Vector2(32, 32)) + +func edit() -> void: + Input.set_custom_mouse_cursor(edit_cursor, Input.CURSOR_ARROW, Vector2(32, 32)) \ No newline at end of file diff --git a/utility/game_state.gd b/utility/game_state.gd index 6817dcc..89325e1 100644 --- a/utility/game_state.gd +++ b/utility/game_state.gd @@ -1,8 +1,5 @@ class_name GameStateManager extends Node -## THIS SHOULD NOT EXIST HERE BUT I AM NOT GOING TO MAKE A NEW CLASS FOR IT RIGHT NOW -@onready var default_cursor : Resource = preload("res://resources/cursors/pointer_a.png") - var placing_drone : bool = false var level_timer : float = 0.0 @@ -15,15 +12,20 @@ var game_over : bool = false ## Game Rules ############################################################## -# Nectar levels - Depleted by snails, increased by bee visits?? Or time, TBD -# TODO: Decide how to renew the nectar levels +# Nectar levels - Depleted by snails, increased by bee nectar deposits # This is capped out at 10... Seems like a good amount +# This has to be at least 1 at all times or the bees dont know wtf is going on +var flower_nectar_level_charge : int = 0 +var flower_nectar_level_charge_required : int = 10 +var max_flower_nectar_level : int = 10 var flower_nectar_level : int = 10 : get: return flower_nectar_level set(value): - if value > 10: + if value > max_flower_nectar_level: flower_nectar_level = 10 + elif value < 1: + flower_nectar_level = 1 else: flower_nectar_level = value @@ -69,11 +71,20 @@ func _process(delta : float) -> void: if level_started and !level_complete and !game_over: level_timer += delta +## For every 10 times the bees deposit nectar it will charge +# the nectar level of the flowers by 1 +func pollenate() -> void: + flower_nectar_level_charge += 1 + if flower_nectar_level_charge >= flower_nectar_level_charge_required: + flower_nectar_level += 1 + flower_nectar_level_charge = 0 + func bee_died() -> void: dead_bees += 1 # Add the nectar to the total gathered nectar func add_nectar(nectar : int) -> void: + pollenate() gathered_nectar += nectar func add_drone() -> void: @@ -107,6 +118,3 @@ func reset() -> void: drones_used = 0 dead_bees = 0 spawn_snails = false - -func reset_cursor() -> void: - Input.set_custom_mouse_cursor(default_cursor, Input.CURSOR_ARROW, Vector2(8, 8))