Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: RoomScript #558

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
Open

Conversation

bolinfest
Copy link
Contributor

This is a demo of something I have been working on, tentatively called RoomScript.

The overarching goal is to define a room in a single file, more in the style of
Delores: A Thimbleweed Park mini-adventure:
https://github.com/grumpygamer/DeloresDev/blob/master/Scripts/Rooms/MainStreet.dinky

Today, Escoria requires a lot of names:

  • Godot "name" of a Node
  • Escoria global_id
  • Path to .esc file

RoomScript eliminates the individual .esc files. It still supports the global_id field,
but it often turns out to be unnecessary, as many scripts can use the existing Node
name rather than the global_id.

The RSCRoom (which is a subclass of ESCRoom) maintains its global id,
but there must be a matching .room file in the same directory. Therefore,
instead of having to declare the path to the .room file as a property of RSCRoom,
we exploit the convention and rely on this logic instead:

static func find_script_file_for_room(room: RSCRoom) -> String:
	# For now, we enforce the convention that the name and Escoria global_id
	# should match, which must also be the name of the subfolder under rooms/,
	# as well as the name of the `.room` file. (If the rooms/ folder becomes
	# too large, we can revisit this.)
	var room_id = room.global_id
	var room_dir = zero_pad_room_id(room_id)
	var res = "res://game/rooms/%s/%s.room" % [room_dir, room_id]
	escoria.logger.debug("room path: %s" % res)
	return res

Within the .room file, there are simple declarations/blocks like event setup {}
and item button {} that contain ESC. Again, this is done in favor of the invidual .esc files.
In practice, many items support only one verb, like look or use, so creating a new
.esc file for each can feel like unnecessary overhead. This is also one less field you
need to enter for each item in the Godot editor.

As you can see, the end result is in game/rooms/room02/room2.room and all the
.esc files for room02 have been eliminated at the top of this stack, yet everything
still works as it did before.

I certainly don't expect this PR to be accepted as-is. As you can see, the logic in
rsc_item.gd and rsc_room.gd is pretty hacky to make this work. Likewise, I had
to create parallel versions of a number of built-in commands in
addons/escoria-roomscript/commands/ in order to support the local names instead
of the global ids. My hope is that we could evolve the framework to support this
style of development, even if it is still an addon.

@dploeger
Copy link
Collaborator

This is pretty awesome. Just to be sure: You're not proposing this as an official plugin with a conversion of the given rooms but rather as a showcase of your own plugin?

I really like what you did here, but I don't see it as an official plugin because we have to maintain ESC and that's already hard. 😉

I'd love to see it as your own Escoria plugin (like the way I did the Escoria ink bridge), so that the "Escoria ecosystem" grows with it.

@StraToN
Copy link
Collaborator

StraToN commented Mar 29, 2022

I find this very interesting. I remember that I wondered if having 1 script per room would be good or not, but I quickly ditched this idea based on my personal tastes: I feared that the script would be cluttered and hard to navigate through, since we currently don't have a good ESC editor on that matter. However, this is only a matter of personal taste and I totally agree that some could believe the contrary (too many files == messy). Maybe there is no perfect solution, I don't know.

My hope is that we could evolve the framework to support this style of development, even if it is still an addon.

[in order to support the local names instead of the global ids]
If I am not wrong, we are using node's name if global_id is empty, but this is not always true as I believe some object require a global_id to be set. What you need here is a modification to make it non-required, right?

@bolinfest
Copy link
Contributor Author

Just to be sure: You're not proposing this as an official plugin with a conversion of the given rooms but rather as a showcase of your own plugin?

Correct: not proposing it as an official plugin, just a showcase. What I would hope to achieve:

  • Be able to upstream API changes elsewhere that make this plugin maintainable. For example, I currently have to override ESCItem._detect_children() to make this work, which is brittle and nonsensical.
  • See if escoria-core wants to take any "inspiration" from this plugin and make it part of the API, such as the alternative way I derive global_id if it is not set. (For sure, that would also help make this plugin more maintainable!)

@bolinfest
Copy link
Contributor Author

@StraToN I think the biggest thing I need is the ability for a different way to resolve identifiers to objects, particulary in ESC. For example, the way I had to rewrite all the commands in this PR so I can intercept the calls and run RSCRoom.rewrite_command_params() should be done in one place somewhere else so the original commands can be used as-is. A somewhat "natural" place to move that would be inside ESCObjectManager, but the current design of ESCObjectManager is not particularly friendly to inheritance, nor is there a clean way to assign a different globals_manager on startup in addons/escoria-core/game/escoria.gd right now. These are all things that could be changed, but I'm not sure what the project would be most open to.

Also, the name resolution I add in RoomScript relies heavily on this bit:

static func _resolve_item_id(item_id: String) -> String:
	var room = find_room_for_item(escoria.main.current_scene)
	if room:
		var global_id = room.resolve_item_id(item_id)
		if global_id:
			return global_id
	return item_id

That is, it goes through escoria.main.current_scene and assumes that it is set and the RSCRoom should be ready and available in the tree when _resolve_item_id() is called. I haven't looked at the work that is currently in flight for #515 yet, though I worry something in there could break some of the current assumptions in my code.

@bolinfest bolinfest requested a review from balloonpopper as a code owner July 18, 2022 20:17
bolinfest added a commit to bolinfest/escoria-demo-game that referenced this pull request Oct 25, 2023
My high-level goal is to use my own scripting language for Escoria.
This goes back to some of my original work on RoomScript:

godot-escoria#558

as well as this prototype to generate ESC from my own scripting language:

https://github.com/bolinfest/alt-esc-prototype/

Because godot-escoria#558 was a large change that was impractical to upstream,
I decided to try to rework things as a much smaller diff so that
I could unblock myself without having to maintain a fork.
The crux of this commit is only a two-line change to `esc_object_manager.gd`.

The rest of the changes in the commit are updates to the demo to illustrate how
the feature could be used, as it changes `r3_l_exit` to be a custom instance of
`ESCItem` that implements the new, optional `generate_events()` function so it
can return its own `Dictionary` to map event names to `ESCEvent` instances.

Though in practice, I would not expect to author this sort of code by hand.
Instead, I would have my own scripting language from which I would generate
files such as `l_door.gd` and `l_door_exit_scene.gd`.

Though as illustrated by `_compile_event_from_esc()`, I could
start by generating ESC instead of going straight to GDScript.
For example, my input language might be:

```
room room03 {
  item l_door {
    event exit_scene {
      // ESC
      change_scene "res://game/rooms/room02/room02.tscn"
    }
  }

  item r_door {
    event exit_scene {
      // ESC
      change_scene "res://game/rooms/room04/room04.tscn"
    }
  }
}
```

And from this, it would be fairly straightforward to generate these two `.gd` files:

```gdscript
# game/rooms/room03/l_door.gd
extends ESCItem

func generate_events() -> Dictionary:
    return {
        "exit_scene": escoria.esc_compiler.compile([
            ":exit_scene",
            "change_scene \"res://game/rooms/room02/room02.tscn\"",
        ]).events["exit_scene"],
    }
```

and:

```gdscript
# game/rooms/room03/r_door.gd
extends ESCItem

func generate_events() -> Dictionary:
    return {
        "exit_scene": escoria.esc_compiler.compile([
            ":exit_scene",
            "change_scene \"res://game/rooms/room04/room04.tscn\"",
        ]).events["exit_scene"],
    }
```

Initially, I admit this does not buy much other than making it possible
to define events for multiple items in one file. Though over time, I
would like to be able to more sophisticated things like you see in:

https://github.com/grumpygamer/DeloresDev/blob/master/Scripts/Rooms/MainStreet.dinky

While I know it is possible to extend ESC by creating my own instances of
`ESCCommand`, I would prefer to have the freedom to experiment with a
broarder array of constructs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants