How do I listen to an event on an object that hasn't been created yet?

In my experience, that’s just the wrong way around for game logic (and distributed simulation in general.)

You should have the trigger thing (in this case, the freeze gun) emit the events (“things got frozen, yo”) and then either use some kind of generalized event bus, or have the emitter decide whom to emit the events to at the time of emitting.

“Decide whom to emit to” is your typical hit scan or impact based weapon; find which entities overlap a ray, or which entities collide with a sphere or something; those entities get the message.

An event bus would instead be a shared mechanism for finding entities that listen to particular events. For example, you could have an “on freeze” event, and each NPC listens on the bus for that event, and each freeze gun sends that event. The event bus can be as easy as a global map from event name to list of weak-pointers-to-listeners (so the map doesn’t keep otherwise dead objects alive.)

The “event” can then take a bunch of different shapes. For “damage,” there are methods built into the Actor system, but for more specialized events, you’ll typically either add some kind of component that knows how to receive the event, or you’ll implement some interface that has appropriate functions for receiving the event. The sender will then typically use dynamic casting/introspection to “get components of kind” or “send to interface if implemented” to deliver the event.

And, because you’re in C++, you really should consider the Gameplay Ability System. It has a whole system set up for triggers, chains of triggers, effects, stacking effects, visualizations, rate limits, and so on and so forth. Once you’re in that system, building particular effects will be quite straightforward and “fit in” to the overall system.

But, even if you don’t, the event should be triggered by the sender dynamically finding which objects should be notified.