Skip to content

Conversation

@wjt
Copy link
Member

@wjt wjt commented Jan 22, 2026

Previously we had one scene with the sequence_puzzle_object.gd script attached
at its root, with a generic white circle as the sprite. This scene was then
instantiated in every scene that uses this puzzle, and on every instance in each
scene, the SpriteFrames were overridden appropriately.

The problem with doing this is that it only really works if the sprite matches
the collision & interaction shapes of the generic asset. (In fact these shapes
did not match the generic asset: they were created based on the musical rocks!)
We either ignored this issue, or used the Editable Children feature to tweak the
collision shapes as needed.

In recent commits this has changed:

  • musical_rock.tscn represents the specific rock that is used in the musician
    quest; it is configured with the rock SpriteFrames and matching shapes.

  • champ_sequence_puzzle_object.tscn uses a subclass of the
    sequence_puzzle_object.gd script, and has its own collision shapes matching the
    sprite in that quest.

  • In Stella, the tortoise is a scene containing six instances of
    stella_tortoise_shell_gem.tscn. Each gem has sequence_puzzle_object.gd attached
    but unlike the other instances it is a Node2D rather than a SolidBody2D. The
    tortoise itself is a single SolidBody2D.

The pattern is that it is more flexible to duplicate the scene so that it can be
customized for the specific puzzle at hand, while reusing the script.

To guide learners in this direction, move the generic object scene into the
NO_EDIT template. Our StoryQuest duplicator will duplicate the scene (but not
the script!) when duplicating the NO_EDIT template. This will make it easier for
the team creating a StoryQuest to adjust the object scene as needed to match
their assets without affecting the broader game.

(In practice not every team has replaced the generic object sprite, so in this
case we will have a duplicate scene for no upside. But scenes are small!)

See #1150 for discussion of
this general approach.


wjt added 2 commits January 22, 2026 12:55
Previously this tool could handle two ways that scene A can reference
some scene B that is also in the NO_EDIT template:

1. As a PackedScene property assigned to a node in scene A
2. Referred to by UID or path in a string property of a node in scene A

However, it could not handle the case where scene B is instantiated into
scene A.

Supporting this allows us to explore a model where, rather than
StoryQuests instantiating a shared scene (such as player.tscn,
throwing_enemy.tscn, or sequence_puzzle_object.tscn) and overriding
properties on each instance of it, they instead contain their own copy
of such scenes, and can customize that copy, once. This makes it much
easier to make changes like adjusting the collision shapes without
accidentally modifying shared scenes used elsewhere in the game.
#1150

Doing this requires several steps:

1. Duplicate the instanced scene B as a new scene C, using existing
   logic;
2. Instance the new scene;
3. Replace the original instance with the new instance, preserving the
   name (which is part of the API of the scene), children, and any
   properties that were overridden.

Node.replace_by() almost does step 3, except that it does not preserve
the name, and it has the very weird behaviour of changing the
replacement node to be an instance of whatever the original node was an
instance of: exactly the opposite of what we are trying to achieve.
Previously we had one scene with the sequence_puzzle_object.gd script attached
at its root, with a generic white circle as the sprite. This scene was then
instantiated in every scene that uses this puzzle, and on every instance in each
scene, the SpriteFrames were overridden appropriately.

The problem with doing this is that it only really works if the sprite matches
the collision & interaction shapes of the generic asset. (In fact these shapes
did not match the generic asset: they were created based on the musical rocks!)
We either ignored this issue, or used the Editable Children feature to tweak the
collision shapes as needed.

In recent commits this has changed:

- musical_rock.tscn represents the specific rock that is used in the musician
  quest; it is configured with the rock SpriteFrames and matching shapes.

- champ_sequence_puzzle_object.tscn uses a subclass of the
  sequence_puzzle_object.gd script, and has its own collision shapes matching the
  sprite in that quest.

- In Stella, the tortoise is a scene containing six instances of
  stella_tortoise_shell_gem.tscn. Each gem has sequence_puzzle_object.gd attached
  but unlike the other instances it is a Node2D rather than a SolidBody2D. The
  tortoise itself is a single SolidBody2D.

The pattern is that it is more flexible to duplicate the scene so that it can be
customized for the specific puzzle at hand, while reusing the script.

To guide learners in this direction, move the generic object scene into the
NO_EDIT template. Our StoryQuest duplicator will duplicate the scene (but not
the script!) when duplicating the NO_EDIT template. This will make it easier for
the team creating a StoryQuest to adjust the object scene as needed to match
their assets without affecting the broader game.

(In practice not every team has replaced the generic object sprite, so in this
case we will have a duplicate scene for no upside. But scenes are small!)

See #1150 for discussion of
this general approach.
@wjt wjt requested review from a team as code owners January 22, 2026 12:56
@wjt wjt marked this pull request as draft January 22, 2026 12:56
@wjt
Copy link
Member Author

wjt commented Jan 22, 2026

Sorry, meant to open this as a draft.

The changes to the duplication logic work in Godot 4.6-rc2 but crash in the current stable release Godot 4.5.1 from Flathub. I have spent several days trying to debug this but I have given up.


copied.resource_path = copy_path

var result := copied.pack(scene)
Copy link
Member Author

@wjt wjt Jan 22, 2026

Choose a reason for hiding this comment

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

With Godot 4.5.1 from Flathub the engine segfaults at this line in the case where scene referenced another scene. I can't reproduce it with a local build of the development branch with debug symbols enabled, but I don't know whether this is because some change between 4.5.1 and 4.6-rc2 has fixed whatever the underlying engine bug is, or because I am building with a different toolchain in a way that masks the bug.

I have tried to boil this down to a smaller, reproducible test case, and found some problems along the way, but ultimately I have spent too long on this and have to set it aside. I'm pretty sure my logic above is correct and the engine is buggy (at some level that is obvious because GDScript should not be able to cause a segfault in the engine).

@github-actions
Copy link

Play this branch at https://play.threadbare.game/branches/endlessm/wjt/move-generic-sequence-puzzle-object-scene-to-no-edit-template.

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

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.

2 participants