From bb5724429a53cee03b6e969898ae74b10a58a144 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 6 Jun 2024 15:49:05 +0100 Subject: [PATCH] Added job component and queue system - Introduced a new JobComponent and JobQueue to manage jobs in the game. - Created two new jobs: DigJob and InfectJob with their respective scripts. - Updated CrystalGlowComponent, FreeCameraComponent, MushroomGlowComponent, WaterEffectComponent to improve logging messages. - Adjusted camera movement limits in FreeCameraGameCameraComponent for better control. - Added FiniteStateMachine class for managing states of entities. - Implemented GlowingIdle state as an example of using the state machine. - Included a utility function to fetch file paths by extension from a directory. --- components/JobComponent.tscn | 10 +++ components/jobs/Dig.tscn | 6 ++ components/jobs/Infect.tscn | 6 ++ components/jobs/scripts/dig.gd | 10 +++ components/jobs/scripts/infect.gd | 10 +++ components/jobs/scripts/job.gd | 11 +++ components/scripts/crystal_glow_component.gd | 8 +- components/scripts/free_camera_component.gd | 8 -- .../free_camera_game_camera_component.gd | 10 +-- components/scripts/job_component.gd | 57 +++++++++++++ components/scripts/job_queue.gd | 5 ++ components/scripts/mushroom_glow_component.gd | 6 +- components/scripts/water_effect_component.gd | 6 +- entities/glowing.tscn | 11 ++- entities/scripts/finite_state_machine.gd | 75 ++++++++++++++++++ entities/scripts/mushroom_glow.gd | 3 - entities/scripts/state.gd | 18 +++++ entities/states/glowling/glowling_idle.gd | 5 ++ levels/test_level.tscn | 9 ++- project.godot | 1 + resources/icons/fsm.png | Bin 0 -> 19946 bytes resources/icons/fsm.png.import | 34 ++++++++ utilities/FileUtilitiesClass.gd | 31 ++++++++ 23 files changed, 311 insertions(+), 29 deletions(-) create mode 100644 components/JobComponent.tscn create mode 100644 components/jobs/Dig.tscn create mode 100644 components/jobs/Infect.tscn create mode 100644 components/jobs/scripts/dig.gd create mode 100644 components/jobs/scripts/infect.gd create mode 100644 components/jobs/scripts/job.gd create mode 100644 components/scripts/job_component.gd create mode 100644 components/scripts/job_queue.gd create mode 100644 entities/scripts/finite_state_machine.gd create mode 100644 entities/scripts/state.gd create mode 100644 entities/states/glowling/glowling_idle.gd create mode 100644 resources/icons/fsm.png create mode 100644 resources/icons/fsm.png.import create mode 100644 utilities/FileUtilitiesClass.gd diff --git a/components/JobComponent.tscn b/components/JobComponent.tscn new file mode 100644 index 0000000..3a335c1 --- /dev/null +++ b/components/JobComponent.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=3 format=3 uid="uid://buadlj7sbutdh"] + +[ext_resource type="Script" path="res://components/scripts/job_component.gd" id="1_lsesq"] +[ext_resource type="Script" path="res://components/scripts/job_queue.gd" id="2_tal7p"] + +[node name="JobComponent" type="Node"] +script = ExtResource("1_lsesq") + +[node name="JobQueue" type="Node" parent="."] +script = ExtResource("2_tal7p") diff --git a/components/jobs/Dig.tscn b/components/jobs/Dig.tscn new file mode 100644 index 0000000..b87dc90 --- /dev/null +++ b/components/jobs/Dig.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://bypebl5w1aivf"] + +[ext_resource type="Script" path="res://components/jobs/scripts/dig.gd" id="1_my7sj"] + +[node name="Dig" type="Node"] +script = ExtResource("1_my7sj") diff --git a/components/jobs/Infect.tscn b/components/jobs/Infect.tscn new file mode 100644 index 0000000..7a9eee2 --- /dev/null +++ b/components/jobs/Infect.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://pbnva2nkgral"] + +[ext_resource type="Script" path="res://components/jobs/scripts/infect.gd" id="1_xpq3i"] + +[node name="Infect" type="Node"] +script = ExtResource("1_xpq3i") diff --git a/components/jobs/scripts/dig.gd b/components/jobs/scripts/dig.gd new file mode 100644 index 0000000..8ced2b8 --- /dev/null +++ b/components/jobs/scripts/dig.gd @@ -0,0 +1,10 @@ +extends Job +class_name DigJob + +func _init() -> void: + job_name = "Dig" + job_description = "Dig some stuff!" + super._init() + +func _ready() -> void: + Log.pr("Dig job added to the scene...") diff --git a/components/jobs/scripts/infect.gd b/components/jobs/scripts/infect.gd new file mode 100644 index 0000000..e461321 --- /dev/null +++ b/components/jobs/scripts/infect.gd @@ -0,0 +1,10 @@ +extends Job +class_name InfectJob + +func _init() -> void: + job_name = "Infect" + job_description = "Infect some roots" + super._init() + +func _ready() -> void: + Log.pr("Infect job added to the scene...") diff --git a/components/jobs/scripts/job.gd b/components/jobs/scripts/job.gd new file mode 100644 index 0000000..5977f8e --- /dev/null +++ b/components/jobs/scripts/job.gd @@ -0,0 +1,11 @@ +extends Node +class_name Job + +signal job_completed +signal job_failed + +var job_name : String +var job_description : String + +func _init() -> void: + pass \ No newline at end of file diff --git a/components/scripts/crystal_glow_component.gd b/components/scripts/crystal_glow_component.gd index 4989152..1234aa4 100644 --- a/components/scripts/crystal_glow_component.gd +++ b/components/scripts/crystal_glow_component.gd @@ -16,13 +16,13 @@ func _ready() -> void: if !tile_map: Log.err("CrystalGlowComponent: TileMap not set") - Log.pr("CrystalGlowComponent ready") - tile_size = tile_map.get_tileset().tile_size tilemap_size = tile_map.get_used_rect().end - tile_map.get_used_rect().position crystal_layer = tile_map.get_layer_id_by_name('Crystals') - Log.pr(crystal_layer) + + Log.pr("CrystalGlowComponent ready, working on layer: ", crystal_layer) + add_crystal_glow() ## Look for crystals in the scene and add the glow to the approprirate angle and colour @@ -46,5 +46,3 @@ func add_crystal_glow() -> void: glow.orientation = orientation crystal_glow_container.add_child(glow) glowing_crystals.append(coords) - - Log.pr("Glowing crystals: ", glowing_crystals) \ No newline at end of file diff --git a/components/scripts/free_camera_component.gd b/components/scripts/free_camera_component.gd index b322fd8..36a4331 100644 --- a/components/scripts/free_camera_component.gd +++ b/components/scripts/free_camera_component.gd @@ -9,20 +9,12 @@ func _ready() -> void: if !tile_map: Log.err("FreeCameraComponent: tile_map is not set") - ## Set the limits for the camera based on the tilemap - #camera.set_limits(0, tile_map.) - # Stop the camera scrolling out of bounds of the map var tile_size : Vector2i = tile_map.get_tileset().tile_size var tilemap_size : Vector2i = tile_map.get_used_rect().end - tile_map.get_used_rect().position var map_size_x : int = tile_size.x * tilemap_size.x var map_size_y : int = tile_size.y * tilemap_size.y - Log.pr("Size Y", map_size_y, get_viewport().size.y) - Log.pr("Size X", map_size_x, get_viewport().size.x) - - #Log.pr(map_size - get_viewport().size.x) - camera.set_limit(SIDE_LEFT, 0) camera.set_limit(SIDE_TOP, 0) camera.set_limit(SIDE_RIGHT, map_size_x) diff --git a/components/scripts/free_camera_game_camera_component.gd b/components/scripts/free_camera_game_camera_component.gd index 0dde4b7..8a22dfd 100644 --- a/components/scripts/free_camera_game_camera_component.gd +++ b/components/scripts/free_camera_game_camera_component.gd @@ -64,18 +64,18 @@ func _physics_process(delta : float) -> void: # If this movement would take us out of clamp we don't want to do it: var position_change : Vector2 = camera_movement * get_zoom() - if (position.x + position_change.x) <= limit_left or\ - (position.x + position_change.x) >= limit_right: + if (position.x + position_change.x) < limit_left or\ + (position.x + position_change.x) > limit_right: camera_movement.x = 0 - if (position.y + position_change.y) <= limit_top or\ - (position.y + position_change.y) >= limit_bottom: + if (position.y + position_change.y) < limit_top or\ + (position.y + position_change.y) > limit_bottom: camera_movement.y = 0 if camera_movement.y != 0 or camera_movement.x != 0: # Update position of the camera. position += camera_movement * get_zoom() - + # Set camera movement to zero, update old mouse position. camera_movement = Vector2(0,0) _prev_mouse_pos = get_local_mouse_position() diff --git a/components/scripts/job_component.gd b/components/scripts/job_component.gd new file mode 100644 index 0000000..9559ac9 --- /dev/null +++ b/components/scripts/job_component.gd @@ -0,0 +1,57 @@ +extends Node +class_name JobComponent + +## Constants +const JOB_FOLDER = "res://components/jobs/" + +## Variables +var job_types : Dictionary = {} + +@onready var queue : JobQueue = $JobQueue + + +func _ready() -> void: + + ## Get the list of job types from the job folder + load_jobs() + add_jobs_to_queue() + add_jobs_to_queue() + add_jobs_to_queue() + + Log.pr("I am the job component, and I am ready.") + + +# Look in the JOB_FOLDER and preload all scenes into a dictionary +func load_jobs() -> void: + var job_files : Array = FileUtil.get_file_paths_by_extension(JOB_FOLDER, "tscn") + + #Log.pr("Job files: ", job_files) + + for file : String in job_files: + #Log.pr("Loading job: ", file) + var job_scene : PackedScene = load(file) + var job_instance : Job = job_scene.instantiate() + + #Log.pr("⚒️ Job Name:", job_instance.job_name, "-- Description:", job_instance.job_description) + + var job_details : Dictionary = { + 'job_name': job_instance.job_name, + 'job_description': job_instance.job_description, + 'job_instance': job_scene + } + + job_types[job_instance.job_name] = job_details + + #Log.pr(job_details) + + Log.pr("Job types: ", job_types) + +func add_jobs_to_queue() -> void: + #Log.pr("This is a debug function!") + + for job : String in job_types.keys(): + #Log.pr("Adding job to queue: ", job) + if job_types[job].job_instance != null: + var job_scene : PackedScene = job_types[job].job_instance + var job_instance : Node = job_scene.instantiate() + queue.add_child(job_instance) diff --git a/components/scripts/job_queue.gd b/components/scripts/job_queue.gd new file mode 100644 index 0000000..fd0c86d --- /dev/null +++ b/components/scripts/job_queue.gd @@ -0,0 +1,5 @@ +extends Node +class_name JobQueue + +func _ready() -> void: + Log.pr("Job Queue ready...") \ No newline at end of file diff --git a/components/scripts/mushroom_glow_component.gd b/components/scripts/mushroom_glow_component.gd index 614258b..127f504 100644 --- a/components/scripts/mushroom_glow_component.gd +++ b/components/scripts/mushroom_glow_component.gd @@ -15,7 +15,7 @@ func _ready() -> void: if !tile_map: Log.err("MushroomGlowComponent: TileMap not set") - Log.pr("MushroomGlowComponent ready") + tile_map.tilemap_updated.connect(add_mushroom_glow) @@ -23,7 +23,9 @@ func _ready() -> void: tilemap_size = tile_map.get_used_rect().end - tile_map.get_used_rect().position mushroom_layer = tile_map.get_layer_id_by_name('Mushrooms') - Log.pr(mushroom_layer) + + Log.pr("MushroomGlowComponent ready, working on layer:", mushroom_layer) + add_mushroom_glow() ## Look for crystals in the scene and add the glow to the approprirate angle and colour diff --git a/components/scripts/water_effect_component.gd b/components/scripts/water_effect_component.gd index 8676839..0c1ce64 100644 --- a/components/scripts/water_effect_component.gd +++ b/components/scripts/water_effect_component.gd @@ -18,13 +18,15 @@ func _ready() -> void: if !tile_map: Log.err("WaterEffectComponent: TileMap not set") - Log.pr("WaterEffectComponent ready") + tile_size = tile_map.get_tileset().tile_size tilemap_size = tile_map.get_used_rect().end - tile_map.get_used_rect().position water_layer = tile_map.get_layer_id_by_name('Water') - Log.pr(water_layer) + + Log.pr("WaterEffectComponent ready, working on layer:", water_layer) + add_water_effects() ## Look for crystals in the scene and add the glow to the approprirate angle and colour diff --git a/entities/glowing.tscn b/entities/glowing.tscn index 1c6cd02..5518388 100644 --- a/entities/glowing.tscn +++ b/entities/glowing.tscn @@ -1,7 +1,9 @@ -[gd_scene load_steps=5 format=3 uid="uid://ddwnkcnncxmjv"] +[gd_scene load_steps=7 format=3 uid="uid://ddwnkcnncxmjv"] [ext_resource type="Script" path="res://entities/scripts/glowling.gd" id="1_aq1kk"] [ext_resource type="Texture2D" uid="uid://b0v24ggq57237" path="res://resources/particles/smallcircle.png" id="2_s3xbi"] +[ext_resource type="Script" path="res://entities/scripts/finite_state_machine.gd" id="3_vqtdm"] +[ext_resource type="Script" path="res://entities/states/glowling/glowling_idle.gd" id="4_2l3e6"] [sub_resource type="Animation" id="Animation_01nc3"] resource_name = "GlowingPulse" @@ -63,3 +65,10 @@ energy = 0.7 shadow_filter = 2 shadow_filter_smooth = 7.5 texture = ExtResource("2_s3xbi") + +[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("initial_state")] +script = ExtResource("3_vqtdm") +initial_state = NodePath("Idle") + +[node name="Idle" type="Node" parent="StateMachine"] +script = ExtResource("4_2l3e6") diff --git a/entities/scripts/finite_state_machine.gd b/entities/scripts/finite_state_machine.gd new file mode 100644 index 0000000..f90ea42 --- /dev/null +++ b/entities/scripts/finite_state_machine.gd @@ -0,0 +1,75 @@ +@icon("res://resources/icons/fsm.png") +extends Node +class_name FiniteStateMachine + +var states : Dictionary = {} +var current_state : State +@export var initial_state : State + +#NOTE This is a generic finite_state_machine, it handles all states, changes to this code will affect + # everything that uses a state machine! + +func _ready() -> void: + for child : State in get_children(): + states[child.name.to_lower()] = child + child.state_transition.connect(change_state) + + if initial_state: + initial_state.enter() + current_state = initial_state + +# Call the current states update function +func _process(delta : float) -> void: + if current_state: + current_state.update(delta) + +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: + var newState : State = states.get(new_state.to_lower()) + + if !newState: + print(new_state + " does not exist in the dictionary of states") + return + + if current_state == newState: + print("State is same, aborting") + return + + # NOTE Calling exit like so: (current_state.Exit()) may cause warnings when flushing queries, like when the enemy is being removed after death. + # call_deferred is safe and prevents this from occuring. We get the Exit function from the state as a callable and then call it in a thread-safe manner + if current_state: + var exit_callable : Callable = Callable(current_state, "exit") + exit_callable.call_deferred() + + newState.enter() + + current_state = newState + +func change_state(source_state : State, new_state_name : String) -> void: + if source_state != current_state: + #print("Invalid change_state trying from: " + source_state.name + " but currently in: " + current_state.name) + #This typically only happens when trying to switch from death state following a force_change + return + + var new_state : State = states.get(new_state_name.to_lower()) + if !new_state: + print("New state is empty") + return + + if current_state: + current_state.exit() + + new_state.enter() + + current_state = new_state diff --git a/entities/scripts/mushroom_glow.gd b/entities/scripts/mushroom_glow.gd index f7795a9..11827d1 100644 --- a/entities/scripts/mushroom_glow.gd +++ b/entities/scripts/mushroom_glow.gd @@ -8,12 +8,9 @@ class_name MushroomGlow @export var colour_name : String = "blue" func _ready() -> void: - Log.pr("And dog said let their be light...") - Log.pr(position, global_position) set_light_colour(colour_name) animation.play("GlowFlicker") - func set_light_colour(colour : String) -> void: if colour == "blue": inner_glow.color = "4cc5fa" diff --git a/entities/scripts/state.gd b/entities/scripts/state.gd new file mode 100644 index 0000000..1fc2726 --- /dev/null +++ b/entities/scripts/state.gd @@ -0,0 +1,18 @@ +@icon("res://resources/icons/fsm.png") +extends Node +class_name State + +signal state_transition + +func enter(_msg : Dictionary = {}) -> void: + pass + +func exit() -> void : + pass + +func update(_delta : float) -> void: + pass + +func physics_update(_delate : float) -> void: + pass + diff --git a/entities/states/glowling/glowling_idle.gd b/entities/states/glowling/glowling_idle.gd new file mode 100644 index 0000000..a92bde0 --- /dev/null +++ b/entities/states/glowling/glowling_idle.gd @@ -0,0 +1,5 @@ +extends State +class_name GlowingIdle + +func enter(_msg : Dictionary = {}) -> void: + Log.pr("I am a glowling and I am idle...") \ No newline at end of file diff --git a/levels/test_level.tscn b/levels/test_level.tscn index 0e12c03..1f44efa 100644 --- a/levels/test_level.tscn +++ b/levels/test_level.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=115 format=3 uid="uid://cbiwnafhckchl"] +[gd_scene load_steps=116 format=3 uid="uid://cbiwnafhckchl"] [ext_resource type="Texture2D" uid="uid://cwkjk7hh7csea" path="res://resources/sCaveTile.png" id="1_au2ii"] [ext_resource type="Material" uid="uid://bpverily2kmvm" path="res://levels/resources/material_desaturate_tileset.tres" id="1_iph1s"] @@ -15,6 +15,7 @@ [ext_resource type="PackedScene" uid="uid://c87qvtq8ho3bb" path="res://components/MushroomGlowComponent.tscn" id="13_uwl47"] [ext_resource type="PackedScene" uid="uid://mawos5ss1cod" path="res://components/WaterEffectComponent.tscn" id="14_x3ue3"] [ext_resource type="PackedScene" uid="uid://htaqjgupmiiu" path="res://components/FreeCameraComponent.tscn" id="15_xajk3"] +[ext_resource type="PackedScene" uid="uid://buadlj7sbutdh" path="res://components/JobComponent.tscn" id="16_qq30h"] [sub_resource type="Environment" id="Environment_ct14l"] background_mode = 3 @@ -4000,6 +4001,9 @@ shader_parameter/noise_scroll_direction = Vector2(1, 0) [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_ct14l") +[node name="FreeCameraComponent" parent="." node_paths=PackedStringArray("tile_map") instance=ExtResource("15_xajk3")] +tile_map = NodePath("../TileMap") + [node name="TileMap" type="TileMap" parent="."] tile_set = SubResource("TileSet_lsdax") format = 2 @@ -4035,5 +4039,4 @@ position = Vector2(2167, 319) [node name="CanvasModulate" type="CanvasModulate" parent="."] color = Color(0.301961, 0.45098, 1, 1) -[node name="FreeCameraComponent" parent="." node_paths=PackedStringArray("tile_map") instance=ExtResource("15_xajk3")] -tile_map = NodePath("../TileMap") +[node name="JobComponent" parent="." instance=ExtResource("16_qq30h")] diff --git a/project.godot b/project.godot index 53de0f1..a54d8a1 100644 --- a/project.godot +++ b/project.godot @@ -18,6 +18,7 @@ config/icon="res://icon.svg" [autoload] GridUtil="*res://utilities/GridUtilitiesClass.gd" +FileUtil="*res://utilities/FileUtilitiesClass.gd" [debug] diff --git a/resources/icons/fsm.png b/resources/icons/fsm.png new file mode 100644 index 0000000000000000000000000000000000000000..262b2e3b030bafb4dd2ddcf6d94722add5db505c GIT binary patch literal 19946 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 literal 0 HcmV?d00001 diff --git a/resources/icons/fsm.png.import b/resources/icons/fsm.png.import new file mode 100644 index 0000000..821c8b4 --- /dev/null +++ b/resources/icons/fsm.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdibamw1s2nel" +path="res://.godot/imported/fsm.png-8987802cee881c94358eee3cb13f0556.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resources/icons/fsm.png" +dest_files=["res://.godot/imported/fsm.png-8987802cee881c94358eee3cb13f0556.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/utilities/FileUtilitiesClass.gd b/utilities/FileUtilitiesClass.gd new file mode 100644 index 0000000..ad714f3 --- /dev/null +++ b/utilities/FileUtilitiesClass.gd @@ -0,0 +1,31 @@ +extends Node +class_name FileUtilitiesClass + +func get_file_paths_by_extension(directoryPath: String, extension: String, recursive: bool = false) -> Array: + + var dir : DirAccess = DirAccess.open(directoryPath) + + if !dir: + printerr("Warning: could not open directory: ", directoryPath) + return [] + + if dir.list_dir_begin() != OK: + printerr("Warning: could not list contents of: ", directoryPath) + return [] + + var filePaths := [] + var fileName := dir.get_next() + + while fileName != "": + if dir.current_is_dir(): + if recursive: + var dirPath : String = dir.get_current_dir() + "/" + fileName + filePaths += get_file_paths_by_extension(dirPath, extension, recursive) + else: + if fileName.get_extension() == extension: + var filePath : String = dir.get_current_dir() + "/" + fileName + filePaths.append(filePath) + + fileName = dir.get_next() + + return filePaths \ No newline at end of file