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.
This commit is contained in:
Dan 2024-06-06 15:49:05 +01:00
parent fc896925d6
commit bb5724429a
23 changed files with 311 additions and 29 deletions

View file

@ -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")

6
components/jobs/Dig.tscn Normal file
View file

@ -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")

View file

@ -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")

View file

@ -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...")

View file

@ -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...")

View file

@ -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

View file

@ -16,13 +16,13 @@ func _ready() -> void:
if !tile_map: if !tile_map:
Log.err("CrystalGlowComponent: TileMap not set") Log.err("CrystalGlowComponent: TileMap not set")
Log.pr("CrystalGlowComponent ready")
tile_size = tile_map.get_tileset().tile_size tile_size = tile_map.get_tileset().tile_size
tilemap_size = tile_map.get_used_rect().end - tile_map.get_used_rect().position tilemap_size = tile_map.get_used_rect().end - tile_map.get_used_rect().position
crystal_layer = tile_map.get_layer_id_by_name('Crystals') 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() add_crystal_glow()
## Look for crystals in the scene and add the glow to the approprirate angle and colour ## 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 glow.orientation = orientation
crystal_glow_container.add_child(glow) crystal_glow_container.add_child(glow)
glowing_crystals.append(coords) glowing_crystals.append(coords)
Log.pr("Glowing crystals: ", glowing_crystals)

View file

@ -9,20 +9,12 @@ func _ready() -> void:
if !tile_map: if !tile_map:
Log.err("FreeCameraComponent: tile_map is not set") 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 # Stop the camera scrolling out of bounds of the map
var tile_size : Vector2i = tile_map.get_tileset().tile_size 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 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_x : int = tile_size.x * tilemap_size.x
var map_size_y : int = tile_size.y * tilemap_size.y 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_LEFT, 0)
camera.set_limit(SIDE_TOP, 0) camera.set_limit(SIDE_TOP, 0)
camera.set_limit(SIDE_RIGHT, map_size_x) camera.set_limit(SIDE_RIGHT, map_size_x)

View file

@ -64,12 +64,12 @@ func _physics_process(delta : float) -> void:
# If this movement would take us out of clamp we don't want to do it: # If this movement would take us out of clamp we don't want to do it:
var position_change : Vector2 = camera_movement * get_zoom() var position_change : Vector2 = camera_movement * get_zoom()
if (position.x + position_change.x) <= limit_left or\ if (position.x + position_change.x) < limit_left or\
(position.x + position_change.x) >= limit_right: (position.x + position_change.x) > limit_right:
camera_movement.x = 0 camera_movement.x = 0
if (position.y + position_change.y) <= limit_top or\ if (position.y + position_change.y) < limit_top or\
(position.y + position_change.y) >= limit_bottom: (position.y + position_change.y) > limit_bottom:
camera_movement.y = 0 camera_movement.y = 0
if camera_movement.y != 0 or camera_movement.x != 0: if camera_movement.y != 0 or camera_movement.x != 0:

View file

@ -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)

View file

@ -0,0 +1,5 @@
extends Node
class_name JobQueue
func _ready() -> void:
Log.pr("Job Queue ready...")

View file

@ -15,7 +15,7 @@ func _ready() -> void:
if !tile_map: if !tile_map:
Log.err("MushroomGlowComponent: TileMap not set") Log.err("MushroomGlowComponent: TileMap not set")
Log.pr("MushroomGlowComponent ready")
tile_map.tilemap_updated.connect(add_mushroom_glow) 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 tilemap_size = tile_map.get_used_rect().end - tile_map.get_used_rect().position
mushroom_layer = tile_map.get_layer_id_by_name('Mushrooms') 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() add_mushroom_glow()
## Look for crystals in the scene and add the glow to the approprirate angle and colour ## Look for crystals in the scene and add the glow to the approprirate angle and colour

View file

@ -18,13 +18,15 @@ func _ready() -> void:
if !tile_map: if !tile_map:
Log.err("WaterEffectComponent: TileMap not set") Log.err("WaterEffectComponent: TileMap not set")
Log.pr("WaterEffectComponent ready")
tile_size = tile_map.get_tileset().tile_size tile_size = tile_map.get_tileset().tile_size
tilemap_size = tile_map.get_used_rect().end - tile_map.get_used_rect().position tilemap_size = tile_map.get_used_rect().end - tile_map.get_used_rect().position
water_layer = tile_map.get_layer_id_by_name('Water') 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() add_water_effects()
## Look for crystals in the scene and add the glow to the approprirate angle and colour ## Look for crystals in the scene and add the glow to the approprirate angle and colour

View file

@ -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="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="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"] [sub_resource type="Animation" id="Animation_01nc3"]
resource_name = "GlowingPulse" resource_name = "GlowingPulse"
@ -63,3 +65,10 @@ energy = 0.7
shadow_filter = 2 shadow_filter = 2
shadow_filter_smooth = 7.5 shadow_filter_smooth = 7.5
texture = ExtResource("2_s3xbi") 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")

View file

@ -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

View file

@ -8,12 +8,9 @@ class_name MushroomGlow
@export var colour_name : String = "blue" @export var colour_name : String = "blue"
func _ready() -> void: func _ready() -> void:
Log.pr("And dog said let their be light...")
Log.pr(position, global_position)
set_light_colour(colour_name) set_light_colour(colour_name)
animation.play("GlowFlicker") animation.play("GlowFlicker")
func set_light_colour(colour : String) -> void: func set_light_colour(colour : String) -> void:
if colour == "blue": if colour == "blue":
inner_glow.color = "4cc5fa" inner_glow.color = "4cc5fa"

18
entities/scripts/state.gd Normal file
View file

@ -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

View file

@ -0,0 +1,5 @@
extends State
class_name GlowingIdle
func enter(_msg : Dictionary = {}) -> void:
Log.pr("I am a glowling and I am idle...")

View file

@ -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="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"] [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://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://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://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"] [sub_resource type="Environment" id="Environment_ct14l"]
background_mode = 3 background_mode = 3
@ -4000,6 +4001,9 @@ shader_parameter/noise_scroll_direction = Vector2(1, 0)
[node name="WorldEnvironment" type="WorldEnvironment" parent="."] [node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_ct14l") 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="."] [node name="TileMap" type="TileMap" parent="."]
tile_set = SubResource("TileSet_lsdax") tile_set = SubResource("TileSet_lsdax")
format = 2 format = 2
@ -4035,5 +4039,4 @@ position = Vector2(2167, 319)
[node name="CanvasModulate" type="CanvasModulate" parent="."] [node name="CanvasModulate" type="CanvasModulate" parent="."]
color = Color(0.301961, 0.45098, 1, 1) color = Color(0.301961, 0.45098, 1, 1)
[node name="FreeCameraComponent" parent="." node_paths=PackedStringArray("tile_map") instance=ExtResource("15_xajk3")] [node name="JobComponent" parent="." instance=ExtResource("16_qq30h")]
tile_map = NodePath("../TileMap")

View file

@ -18,6 +18,7 @@ config/icon="res://icon.svg"
[autoload] [autoload]
GridUtil="*res://utilities/GridUtilitiesClass.gd" GridUtil="*res://utilities/GridUtilitiesClass.gd"
FileUtil="*res://utilities/FileUtilitiesClass.gd"
[debug] [debug]

BIN
resources/icons/fsm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -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

View file

@ -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