Skip to content

Conversation

@wjt
Copy link
Member

@wjt wjt commented Feb 6, 2026

Previously, all instances of the "fill game" used the same Projectile
scene. To customise its appearance, the ThrowingEnemy scene had a whole
series of properties that would be assigned each instantiated
projectile, in addition to assigning a label & optional modulate color
based on the target barrel.

This is not a very "Godot-ish" way to do it. Conceptually,
different projectiles should be different scenes that have similar
structures and reuse common scripts. Instead, we have had to manually
re-export properties from deep inside the projectile scene, and
propagate their values from the ThrowingEnemy scene.

Since commit 402f2ab
("ThrowingEnemy: Export the projectile scene") it has been possible to
customise which projectile scene is instantiated by any specific enemy,
but this isn't enough either for Stella or for Champ. In Stella, we
wanted the invisible enemies to throw, each time, a random choice
between a starfish (which matches the ship "barrel") or one of three
fish (which don't). But this wasn't possible. Similarly in Champ we want
the three different colours of fish to potentially be different sprites;
they currently happen to be recoloured versions of the same asset that
could be done with the projectile modulate logic but we don't want to
limit contestants to this.

So:

  1. For each StoryQuest that previously reused the generic projectile
    scene, create one (or more!) scenes specific to the quest. This does
    introduce some duplication but I decided that the scenes are small
    enough that using inherited scenes is more trouble than it's worth.

  2. Similarly, make a dedicated ink blob projectile scene for the core
    game.

  3. Move the generic projectile scene to the NO_EDIT template, so that it
    will be duplicated when creating a new StoryQuest.

  4. Remove the sprite_frames and hit_sound_stream properties from
    projectile.gd: these can be set by modifying the scene.

  5. Remove the projectile_sprite_frames, projectile_hit_sound_stream,
    projectile_small_fx_scene, projectile_big_fx_scene, and
    projectile_trail_fx_scene properties from throwing_enemy.gd: these
    can be set by modifying the projectile scene that it spawns.

  6. Add the ability to spawn different projectile scenes based on the
    target label. This is adapted from similar code in the Champ quest to
    use different sprite frames per label. Use this in Champ and Stella.
    In both cases define a custom throwing enemy scene, this time as an
    inherited scene: there are huge animations in the enemy scene so in
    this case I thought this was worth it.

  7. Break the projectile-spawning code up a little. Having done this,
    simplify some of the StoryQuest-specific projectile customisations.

Resolves #1873
Resolves #1874

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

Play this branch at https://play.threadbare.game/branches/endlessm/wjt/customise-projectile-via-scene-only.

(This launches the game from the start, not directly at the change(s) in this pull request.)

@wjt wjt force-pushed the wjt/customise-projectile-via-scene-only branch from a6ef16a to 7550a4f Compare February 6, 2026 19:06
## When launching a projectile, if the chosen label is found in this dictionary,
## the associated scene will be used. If not, the default [member projectile_scene]
## will be used.
@export var projectile_scene_for_label: Dictionary[String, PackedScene]:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a "colo(u)r of bikeshed" situation but maybe this should be projectile_scene_per_label. I want projectile_scene to be at the start of the property name because all properties in the Projectile group must begin with projectile_ for their names to be shortened in the inspector (#1869).

Comment on lines -28 to -40
script/source = "class_name ChampFillGameLogic
extends FillGameLogic
#
#@export var lab: String = \"\"
#
#func _ready() -> void:
#var color_per_label = {\"a\": Color(.3,.5,6), \"b\": Color(.9,.1,6),\"c\": Color(.1,.8,.2)}
#for enemy: ThrowingEnemy in get_tree().get_nodes_in_group(\"throwing_enemy\"):
#enemy.allowed_labels = allowed_labels
#enemy.color_per_label = color_per_label
"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This embedded script doesn't do anything so I removed it in passing.

editor_draw_limits = true

[node name="ThrowingEnemies" type="Node2D" parent="OnTheGround" unique_id=1014610845]
visible = false
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this to the AnimatedSprite2D inside the enemy scene.

@wjt wjt force-pushed the wjt/customise-projectile-via-scene-only branch 2 times, most recently from b403d90 to 9f122c0 Compare February 10, 2026 11:26
Previously, all instances of the "fill game" used the same Projectile
scene. To customise its appearance, the ThrowingEnemy scene had a whole
series of properties that would be assigned each instantiated
projectile, in addition to assigning a label & optional modulate color
based on the target barrel.

This is not a very "Godot-ish" way to do it. Conceptually,
different projectiles should be different scenes that have similar
structures and reuse common scripts. Instead, we have had to manually
re-export properties from deep inside the projectile scene, and
propagate their values from the ThrowingEnemy scene.

Since commit 402f2ab
("ThrowingEnemy: Export the projectile scene") it has been possible to
customise which projectile scene is instantiated by any specific enemy,
but this isn't enough either for Stella or for Champ. In Stella, we
wanted the invisible enemies to throw, each time, a random choice
between a starfish (which matches the ship "barrel") or one of three
fish (which don't). But this wasn't possible. Similarly in Champ we want
the three different colours of fish to potentially be different sprites;
they currently happen to be recoloured versions of the same asset that
could be done with the projectile modulate logic but we don't want to
limit contestants to this.

So:

1. For each StoryQuest that previously reused the generic projectile
   scene, create one (or more!) scenes specific to the quest. This does
   introduce some duplication but I decided that the scenes are small
   enough that using inherited scenes is more trouble than it's worth.

2. Similarly, make a dedicated ink blob projectile scene for the core
   game.

3. Move the generic projectile scene to the NO_EDIT template, so that it
   will be duplicated when creating a new StoryQuest.

4. Remove the sprite_frames and hit_sound_stream properties from
   projectile.gd: these can be set by modifying the scene.

5. Remove the projectile_sprite_frames, projectile_hit_sound_stream,
   projectile_small_fx_scene, projectile_big_fx_scene, and
   projectile_trail_fx_scene properties from throwing_enemy.gd: these
   can be set by modifying the projectile scene that it spawns.

6. Add the ability to spawn different projectile scenes based on the
   target label. This is adapted from similar code in the Champ quest to
   use different sprite frames per label. Use this in Champ and Stella.
   In both cases define a custom throwing enemy scene, this time as an
   inherited scene: there are huge animations in the enemy scene so in
   this case I thought this was worth it.

7. Break the projectile-spawning code up a little. Having done this,
   simplify some of the StoryQuest-specific projectile customisations.

Resolves #1873
Resolves #1874
@wjt wjt force-pushed the wjt/customise-projectile-via-scene-only branch from 9f122c0 to d0bbbcf Compare February 10, 2026 11:31
@wjt wjt marked this pull request as ready for review February 10, 2026 11:37
@wjt wjt requested review from a team as code owners February 10, 2026 11:37
@wjt
Copy link
Member Author

wjt commented Feb 10, 2026

I gave a brief screenshare demo of this change to @jgbourque. Even if we think this is an improvement for learners building StoryQuests, the issue is that this will mean that some guides & videos are now out of date. And we cannot afford to re-record every video for every change to the game.

We agreed that it's not necessary. @jgbourque's plan is to add some general note to this effect, and also explicitly call out to learners that we are gradually changing the customisation model to be "edit a scene" not "change properties on a shared scene". So I think from a learning team perspective we can move ahead with this change.

# SPDX-FileCopyrightText: 2025 Game-Lab-5-0-UTP-Group-1-Team-1 Contributors
# SPDX-License-Identifier: MPL-2.0

@tool
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this Godot warns that a non-@tool script extends a @tool script.

Comment on lines -119 to -120
if disabled_sprite_frames:
animated_sprite_2d.sprite_frames = disabled_sprite_frames
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that in the one place this is used, the two SpriteFrames resources are the same.

@manuq
Copy link
Collaborator

manuq commented Feb 11, 2026

This is great and how it should have been since the beginning!

Copy link
Collaborator

@manuq manuq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering that we had an OK from the learning point of view, I think we should merge it.

@wjt wjt merged commit 8120382 into main Feb 11, 2026
6 checks passed
@wjt wjt deleted the wjt/customise-projectile-via-scene-only branch February 11, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fill game: Allow varying projectile (appearance) by label Change throwing enemy projectile customisation

2 participants