From 872ce5673f8ce8a5d14d8b6e42893687b8dfd426 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Mon, 29 Apr 2024 01:29:33 +0200 Subject: [PATCH] Complete new summoning circle customizer --- behaviours/summoning_circle.gd | 72 +++++++++++++++++ behaviours/summoning_circle.svg | 39 +++++++++ behaviours/summoning_circle.svg.import | 37 +++++++++ behaviours/summoning_circle.tscn | 6 ++ behaviours/summoning_recipe.gd | 79 ++++++++++++------- behaviours/summoning_recipe.svg | 14 ++-- entities/sheep.tscn | 1 + entities/summoning_circle.gd | 29 ------- entities/summoning_circle.tscn | 34 -------- ...cle.png => summoning_circle_pentagram.png} | 0 ... => summoning_circle_pentagram.png.import} | 6 +- entities/summoning_circle_pentagram.tscn | 46 +++++++++++ scenes/game/main_game.tscn | 6 +- 13 files changed, 262 insertions(+), 107 deletions(-) create mode 100644 behaviours/summoning_circle.gd create mode 100644 behaviours/summoning_circle.svg create mode 100644 behaviours/summoning_circle.svg.import create mode 100644 behaviours/summoning_circle.tscn delete mode 100644 entities/summoning_circle.gd delete mode 100644 entities/summoning_circle.tscn rename entities/{summoning_circle.png => summoning_circle_pentagram.png} (100%) rename entities/{summoning_circle.png.import => summoning_circle_pentagram.png.import} (67%) create mode 100644 entities/summoning_circle_pentagram.tscn diff --git a/behaviours/summoning_circle.gd b/behaviours/summoning_circle.gd new file mode 100644 index 0000000..2723260 --- /dev/null +++ b/behaviours/summoning_circle.gd @@ -0,0 +1,72 @@ +@icon("res://behaviours/summoning_circle.svg") +extends Node2D +class_name SummoningCircle + + +## Emitted when a recipe is matched, before the components are sacrificed. +signal recipe_matched(m: SummoningRecipe.Match, recipe: SummoningRecipe) + + +## The [SacrificeStone]s part of this summoning circle. +var stones: Array[SacrificeStone] = [] + +## The [SummoningRecipe]s performable by this summoning circle. +var recipes: Array[SummoningRecipe] = [] + +var _recipes_matched_signals: Array[Callable] = [] + + +## Refresh the value of [field stones], and reconnect signals accordingly. +func refresh_stones() -> void: + # Disconnect signals + for stone in stones: + if stone == null: + return + if stone.sacrifice_changed.is_connected(_on_sacrifice_changed): + stone.sacrifice_changed.disconnect(_on_sacrifice_changed) + # Find the new stones + stones.assign( + find_children("*", "SacrificeStone", true, false) + ) + # Reconnect signals + for stone in stones: + stone.sacrifice_changed.connect(_on_sacrifice_changed) + +## Refresh the value of [field recipes], and reconnect signals accordingly. +func refresh_recipes() -> void: + # Disconnect signals + var idx: int = 0 + for recipe in recipes: + var callable = _recipes_matched_signals[idx] + if recipe.matched.is_connected(callable): + recipe.matched.disconnect(callable) + idx += 1 + _recipes_matched_signals = [] + # Find the new recipes + recipes.assign( + find_children("*", "SummoningRecipe", true, false) + ) + # Reconnect signals + for recipe in recipes: + var callable = _on_recipe_matched.bind(recipe) + recipe.matched.connect(callable) + _recipes_matched_signals.push_back(callable) + + +func _ready() -> void: + refresh_stones() + refresh_recipes() + +func _on_sacrifice_changed(_entity: Node2D) -> void: + for recipe in recipes: + var entities: Array[Node2D] = [] + entities.assign( + stones.map(func(stone): return stone.entity) + ) + if recipe.do_match(entities): + break + +func _on_recipe_matched(m: SummoningRecipe.Match, _recipe: SummoningRecipe) -> void: + recipe_matched.emit() + for sacrificable in m.sacrificables: + sacrificable.sacrifice() diff --git a/behaviours/summoning_circle.svg b/behaviours/summoning_circle.svg new file mode 100644 index 0000000..9076e8d --- /dev/null +++ b/behaviours/summoning_circle.svg @@ -0,0 +1,39 @@ + + + + + + + diff --git a/behaviours/summoning_circle.svg.import b/behaviours/summoning_circle.svg.import new file mode 100644 index 0000000..6c6cd3b --- /dev/null +++ b/behaviours/summoning_circle.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bmgfh5q206kpm" +path="res://.godot/imported/summoning_circle.svg-f06b755c16ca8fe188e1218d3d1d14aa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://behaviours/summoning_circle.svg" +dest_files=["res://.godot/imported/summoning_circle.svg-f06b755c16ca8fe188e1218d3d1d14aa.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 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/behaviours/summoning_circle.tscn b/behaviours/summoning_circle.tscn new file mode 100644 index 0000000..c5566ac --- /dev/null +++ b/behaviours/summoning_circle.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://xj0pa287n1wo"] + +[ext_resource type="Script" path="res://behaviours/summoning_circle.gd" id="1_x3bxd"] + +[node name="SummoningCircle" type="Node2D"] +script = ExtResource("1_x3bxd") diff --git a/behaviours/summoning_recipe.gd b/behaviours/summoning_recipe.gd index 53db5ab..20658a7 100644 --- a/behaviours/summoning_recipe.gd +++ b/behaviours/summoning_recipe.gd @@ -3,59 +3,80 @@ extends Node class_name SummoningRecipe -## A [Node] describing a possible recipe that can be performed with the [SummoningCircle]. +## A [Node] describing a possible ingredients that can be performed with the [SummoningCircle]. -## Emitted when [method do_match] is called, and the [field recipe] is successfully matched. -signal matched +## Emitted when [method do_match] is called, and the [field ingredients] is successfully matched. +signal matched(match: Match) ## How [Sacrificable]s should be matched. @export var mode := Mode.NONE ## Which [Sacrificable]s should be matched. -@export var recipe: Array[StringName] = [] +@export var ingredients: Array[StringName] = [] -func check_match(inputs: Array[StringName]) -> bool: - var matching: Array[bool] = recipe.map(func(): return false) - # This is awful, but I am too lazy to implement something better - # why is there no enumerate Q_Q - for input in inputs: - var idx = 0 - for matchable in recipe: - if input == matchable and not matching[idx]: - matching[idx] = true +func get_match(inputs: Array[Node2D]) -> Match: + var matched_ingredients: Array[Sacrificable] = [] + # Find the ingredients + for ingredient in ingredients: + var matched_this = false + for input in inputs: + if input == null: continue - idx += 1 + for _sacrificable in input.find_children("*", "Sacrificable", true, false): + var sacrificable: Sacrificable = _sacrificable as Sacrificable + if sacrificable in matched_ingredients: + continue + if sacrificable.kind == ingredient: + matched_ingredients.push_back(sacrificable) + matched_this = true + break + if matched_this: + break + if not matched_this: + matched_ingredients.push_back(null) # Use one of the various modes - # why do i have to do this match mode: Mode.NONE: - return false + return null Mode.ANY: - return matching.any(func(value): return value) + if matched_ingredients.any(func(value): return value != null): + return Match.create(matched_ingredients) + else: + return null Mode.ALL: - return matching.all(func(value): return value) - Mode.EXACT: - return matching.all(func(value): return value) and len(inputs) == len(recipe) + if matched_ingredients.all(func(value): return value != null): + return Match.create(matched_ingredients) + else: + return null _: # Static analysis go home you're drunk - return false + return null -func do_match(inputs: Array[StringName]) -> void: - if check_match(inputs): - matched.emit() +func do_match(inputs: Array[Node2D]) -> Match: + var m = get_match(inputs) + if m != null: + matched.emit(m) + return m + + +class Match: + var sacrificables: Array[Sacrificable] + + static func create(s: Array[Sacrificable]) -> Match: + var this = Match.new() + this.sacrificables = s + return this enum Mode { - ## Never match the recipe. + ## Never match the ingredients. NONE = 0, - ## Match the recipe if a single one of the sacrificables is matched. + ## Match the ingredients if a single one of the sacrificables is matched. ANY = 1, - ## Match the recipe if all the sacrificables are matched, even if more are present. + ## Match the ingredients if all the sacrificables are matched, even if more are present. ALL = 2, - ## Match the recipe if all the sacrificables are matched AND there are no other sacrificables present. - EXACT = 3, } diff --git a/behaviours/summoning_recipe.svg b/behaviours/summoning_recipe.svg index e675f0a..614ebc2 100644 --- a/behaviours/summoning_recipe.svg +++ b/behaviours/summoning_recipe.svg @@ -3,7 +3,7 @@ viewBox="0 0 512 512" version="1.1" id="svg1" - sodipodi:docname="summoning_recipe.svg" + sodipodi:docname="summoning_circle.svg" width="16" height="16" inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)" @@ -22,9 +22,9 @@ inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" - inkscape:zoom="13.171875" - inkscape:cx="5.4661922" - inkscape:cy="7.0225386" + inkscape:zoom="5.5" + inkscape:cx="-4.9090909" + inkscape:cy="27.909091" inkscape:window-width="1920" inkscape:window-height="1020" inkscape:window-x="1280" @@ -33,11 +33,7 @@ inkscape:current-layer="svg1" /> - diff --git a/entities/sheep.tscn b/entities/sheep.tscn index 590e115..d60d1a3 100644 --- a/entities/sheep.tscn +++ b/entities/sheep.tscn @@ -325,6 +325,7 @@ stream = ExtResource("16_nswfl") [node name="FallSound" type="AudioStreamPlayer2D" parent="MovementDrag"] stream = ExtResource("17_8kst2") +[connection signal="sacrificed" from="Sacrificable" to="." method="queue_free"] [connection signal="move" from="MovementIdle" to="." method="_on_move"] [connection signal="move_disabled" from="MovementIdle" to="MovementIdle/BoredTimer" method="stop"] [connection signal="move_enabled" from="MovementIdle" to="MovementIdle/BoredTimer" method="start"] diff --git a/entities/summoning_circle.gd b/entities/summoning_circle.gd deleted file mode 100644 index c7ade52..0000000 --- a/entities/summoning_circle.gd +++ /dev/null @@ -1,29 +0,0 @@ -extends Node2D -class_name SummoningCircle - - -## The [SacrificeStone]s part of this summoning circle. -var stones: Array[SacrificeStone] = [] - - -## Refresh the value of [field stones], and reconnect signals accordingly. -func refresh_stones() -> void: - stones.map(func(stone: SacrificeStone): - if stone == null: - return - if stone.sacrifice_changed.is_connected(self._on_sacrifice_changed): - stone.sacrifice_changed.disconnect(self._on_sacrifice_changed) - ) - stones.assign( - find_children("*", "SacrificeStone", true, false) - ) - stones.map(func(stone: SacrificeStone): - stone.sacrifice_changed.connect(self._on_sacrifice_changed) - ) - - -func _ready() -> void: - refresh_stones() - -func _on_sacrifice_changed(_entity: Node2D) -> void: - Log.w(self, "Sacrifice has changed, but no summoning function is implemented.") diff --git a/entities/summoning_circle.tscn b/entities/summoning_circle.tscn deleted file mode 100644 index 61eabbe..0000000 --- a/entities/summoning_circle.tscn +++ /dev/null @@ -1,34 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://xj0pa287n1wo"] - -[ext_resource type="Texture2D" uid="uid://n0wj20mduwy8" path="res://entities/summoning_circle.png" id="1_c6jmb"] -[ext_resource type="Script" path="res://entities/summoning_circle.gd" id="1_x3bxd"] -[ext_resource type="PackedScene" uid="uid://ddpo03rb6068c" path="res://entities/sacrifice_stone.tscn" id="2_dwkfn"] -[ext_resource type="PackedScene" uid="uid://tx1qi6ahlxjp" path="res://behaviours/spawner.tscn" id="3_p6s0q"] - -[node name="SummoningCircle" type="Node2D"] -script = ExtResource("1_x3bxd") - -[node name="Spawner" parent="." instance=ExtResource("3_p6s0q")] -unique_name_in_owner = true - -[node name="Sprite" type="Sprite2D" parent="."] -position = Vector2(2, 4) -scale = Vector2(2, 2) -texture = ExtResource("1_c6jmb") - -[node name="SacrificeStones" type="Node2D" parent="."] - -[node name="StoneUp" parent="SacrificeStones" instance=ExtResource("2_dwkfn")] -position = Vector2(0, -108) - -[node name="StoneTopLeft" parent="SacrificeStones" instance=ExtResource("2_dwkfn")] -position = Vector2(-92, -64) - -[node name="StoneTopRight" parent="SacrificeStones" instance=ExtResource("2_dwkfn")] -position = Vector2(92, -64) - -[node name="StoneBottomLeft" parent="SacrificeStones" instance=ExtResource("2_dwkfn")] -position = Vector2(-75, 30) - -[node name="StoneBottomRight" parent="SacrificeStones" instance=ExtResource("2_dwkfn")] -position = Vector2(75, 30) diff --git a/entities/summoning_circle.png b/entities/summoning_circle_pentagram.png similarity index 100% rename from entities/summoning_circle.png rename to entities/summoning_circle_pentagram.png diff --git a/entities/summoning_circle.png.import b/entities/summoning_circle_pentagram.png.import similarity index 67% rename from entities/summoning_circle.png.import rename to entities/summoning_circle_pentagram.png.import index c3f0e0f..1a83148 100644 --- a/entities/summoning_circle.png.import +++ b/entities/summoning_circle_pentagram.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://n0wj20mduwy8" -path="res://.godot/imported/summoning_circle.png-a9e8e58bff05ee4e15de5d48253b8a5d.ctex" +path="res://.godot/imported/summoning_circle_pentagram.png-2b4ad22e35663173dd75a1efb3a64309.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://entities/summoning_circle.png" -dest_files=["res://.godot/imported/summoning_circle.png-a9e8e58bff05ee4e15de5d48253b8a5d.ctex"] +source_file="res://entities/summoning_circle_pentagram.png" +dest_files=["res://.godot/imported/summoning_circle_pentagram.png-2b4ad22e35663173dd75a1efb3a64309.ctex"] [params] diff --git a/entities/summoning_circle_pentagram.tscn b/entities/summoning_circle_pentagram.tscn new file mode 100644 index 0000000..e203f82 --- /dev/null +++ b/entities/summoning_circle_pentagram.tscn @@ -0,0 +1,46 @@ +[gd_scene load_steps=7 format=3 uid="uid://cgpwig0rd08vh"] + +[ext_resource type="Script" path="res://behaviours/summoning_circle.gd" id="1_l5mec"] +[ext_resource type="PackedScene" uid="uid://tx1qi6ahlxjp" path="res://behaviours/spawner.tscn" id="2_xq0wr"] +[ext_resource type="Texture2D" uid="uid://n0wj20mduwy8" path="res://entities/summoning_circle_pentagram.png" id="3_stpdd"] +[ext_resource type="PackedScene" uid="uid://ddpo03rb6068c" path="res://entities/sacrifice_stone.tscn" id="4_qyef2"] +[ext_resource type="PackedScene" uid="uid://ufjnfj3itypj" path="res://behaviours/summoning_recipe.tscn" id="5_jbk35"] +[ext_resource type="PackedScene" uid="uid://4d3ksr3171x4" path="res://entities/imp.tscn" id="6_utwxo"] + +[node name="SummoningCirclePentagram" type="Node2D"] +script = ExtResource("1_l5mec") + +[node name="Sprite" type="Sprite2D" parent="."] +position = Vector2(2, 4) +scale = Vector2(2, 2) +texture = ExtResource("3_stpdd") + +[node name="Stones" type="Node2D" parent="."] + +[node name="StoneUp" parent="Stones" instance=ExtResource("4_qyef2")] +position = Vector2(0, -108) + +[node name="StoneTopLeft" parent="Stones" instance=ExtResource("4_qyef2")] +position = Vector2(-92, -64) + +[node name="StoneTopRight" parent="Stones" instance=ExtResource("4_qyef2")] +position = Vector2(92, -64) + +[node name="StoneBottomLeft" parent="Stones" instance=ExtResource("4_qyef2")] +position = Vector2(-75, 30) + +[node name="StoneBottomRight" parent="Stones" instance=ExtResource("4_qyef2")] +position = Vector2(75, 30) + +[node name="Recipes" type="Node" parent="."] + +[node name="FiveSheep" parent="Recipes" instance=ExtResource("5_jbk35")] +mode = 2 +ingredients = Array[StringName]([&"Sheep", &"Sheep", &"Sheep", &"Sheep", &"Sheep"]) + +[node name="Spawners" type="Node2D" parent="."] + +[node name="Imp" parent="Spawners" instance=ExtResource("2_xq0wr")] +scene = ExtResource("6_utwxo") + +[connection signal="matched" from="Recipes/FiveSheep" to="Spawners/Imp" method="spawn" unbinds=1] diff --git a/scenes/game/main_game.tscn b/scenes/game/main_game.tscn index b79deb6..39ce024 100644 --- a/scenes/game/main_game.tscn +++ b/scenes/game/main_game.tscn @@ -15,7 +15,7 @@ [ext_resource type="PackedScene" uid="uid://cmemgijh6nfmk" path="res://entities/chupacabra.tscn" id="11_ixo4x"] [ext_resource type="PackedScene" uid="uid://dnjtduk0hla3f" path="res://entities/watcher.tscn" id="14_8rumi"] [ext_resource type="PackedScene" uid="uid://gl4umoff474y" path="res://entities/cthulhu.tscn" id="15_k41qf"] -[ext_resource type="PackedScene" uid="uid://xj0pa287n1wo" path="res://entities/summoning_circle.tscn" id="16_2s71o"] +[ext_resource type="PackedScene" uid="uid://cgpwig0rd08vh" path="res://entities/summoning_circle_pentagram.tscn" id="16_nhsrb"] [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_058kb"] texture = ExtResource("2_o7bg5") @@ -948,5 +948,5 @@ position = Vector2(189, 171) [node name="Cthulhu" parent="." instance=ExtResource("15_k41qf")] position = Vector2(226, -137) -[node name="SummoningCircle" parent="." instance=ExtResource("16_2s71o")] -position = Vector2(-382, -89) +[node name="SummoningCirclePentagram" parent="." instance=ExtResource("16_nhsrb")] +position = Vector2(-321, -191)