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 0000000..262b2e3 Binary files /dev/null and b/resources/icons/fsm.png differ 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