Don't know if this would be applicable for your system, or maybe you've already tried something like this, but I've been implementing my events using Callables (basically function references in Godot). I was annoyed by my original approach, which was to have the main event loop be responsible for event dispatch logic, and an Event object being sort of "instructions" to be read and executed. Having the Event object execute its own logic is surely an improvement but faces similar long-term complexity if it has to know everything or maintain derived types.
Callables kind of solve this complexity. The main event loop knows nothing, and is only responsible for running a Callable and awaiting a signal to proceed to the next one (sometimes have to wait for user actions like dialogue box dismissal, or signals like timer timeout and animation/tween finished), and the Callables themselves only dispatch event logic, and are not responsible for implementation.
For the most part, these Callables are just signal emitters that get appended to the event queue. Most are just like
EventBus.signal_name.emit.bind(args)
but you can use normal functions, lambdas, and I think closures should work if you need them. A complex event could be loosely composed of several general purpose Callables. The response to a signal is handled locally by individual systems that subscribe to it, stuff like UI or spawn managers, helping keep things decoupled.
I'm finding this approach very freeing and expressive, and also tidier than I expected. I sort of stumbled into it in my last jam game, staring down the path of cutscene god objects in horror, and then remembered a chat with another GBJammer about callbacks. My next game's cutscene system will build on this from the ground up, and hopefully result in lean, flexible code.