InvokedCoroutine Instantiation

I’d like to store an InvokedCoroutine in my :sk: class but am unsure how I instantiate the :sk: variable in my constructor. I don’t see any constructors for this type. Expected something like:

@stored_cor : InvokedCoroutine!...

You are correct - the InvokedCoroutine does not currently have a constructor. If a constructor would be useful, we could see about adding one in a future update.

Will you later be replacing invoked coroutine stored in @stored_cor with a new invoked coroutine using a rebind : or did you want to make an empty one that you later invoke or something such as that?

Since a constructor is a method, one way you could bind @stored_cor to a invoked coroutine is:

// _wait with a negative value completes immediately so
// this will create a stale/completed invoked coroutine.
@stored_cor : branch _wait(-1)

// If you want the _wait to be still running just call it with
// the default 0 argument which will complete in 1 frame.
@stored_cor : branch _wait

This might be a bit of a hack or it might be just what you are looking for.

What is your end goal with your stored invoked coroutine?

1 Like

Ok, this is exactly what I tried as a hack. Turns out my oversight was that I forgot to call superclass constructors in a few inherited classes.

My end goal is to implement a time dilation mechanic (freezing enemies, having them slowly thaw). The easiest way I could come up with is every time they’re hit to fire off a method that reduces their dilation coefficient.

(Real amount) 
[
  @actor_time_dilation := MathLib.fmax(@actor_time_dilation - amount, @time_dilation_minimum)
  
  if @time_dilation_co.valid?
  [
    @time_dilation_co.abort
  ]
  @time_dilation_co : branch _wait_time_dilation_cycle
]

It launches a coroutine to handle reverse dilation after a designer specified delay:

[
  println("Dilation started")
  _wait(@time_dilation_reset_delay)

  loop
  [
    @actor_time_dilation := MathLib.fmin(@actor_time_dilation + @time_dilation_recharge_rate * GameLib.world_delta_seconds, 1.0)
    [exit] when @actor_time_dilation = 1.0
    _wait
  ]
  
  println("Time Dilation finished")
]

I couldn’t figure out a good way to handle resetting state in the coroutine, so aborting and launching a new coroutine made sense. Seems to work well so far with just 100 live actors.

It looks pretty good to me.

I can see that a coroutine_running?(coro_name) method would be useful. :madsci:

The main tricky part looks like that initial delay in _wait_time_dilation_cycle().

The way you are resetting things by keeping track of a running coroutine and aborting it if necessary may even be simpler, though an alternate methodology would be to add a @time_dilation_remaining and have it count down to zero per frame or by some smallish time interval. Then you would know if _wait_time_dilation_cycle() is running if @time_dilation_remaining is not 0.0 or if @actor_time_dilation is not 1.0. If it is running you would just adjust the values and the coroutine would adapt. However, what you are doing is perhaps the simplest thing.

You could use coroutine closures and modify the captured values or query data members in the objects that it is operating on to communicate with it. Specific coroutines, as you have done, are probably easier in this case.

You could also try racing it with an @abort_dilation?._wait_true though that seems a bit much. I only mention it to get some alternative ideas flowing. [Note that _wait_true() doesn’t wake the instant a Boolean changes - it waits a particular interval. It would also be a good idea for us to add an invoked coroutine equivalent or some mechanism that would instantaneously wake.]

In a future update we plan to make the omission of superclass constructors in subclass constructors a warning or an error or even call them automatically.

1 Like

Thanks for the idea generation. I see a few paths through the woods but I think I’m leaning towards the coroutine abort solution just because I like it when things do nothing :wink:

A warning seems like it would do for me. I’m trying to imagine a scenario where you might want to override or omit the superclass constructor in some way. It happens occasionally on the C++ side.

I am also a big fan of doing nothing. :madsci:

As for enhanced superclass constructor (and destructor) logic, we’ll deliberate deeper when we near implementation though the mechanism we are leaning towards is the following:

  • if the superclass constructor is omitted, implicitly call the default constructor before the subclass constructor is called
  • if the superclass constructor is explicitly called this allows you to call any superclass named or default constructor or change the order when it is called
  • if you want to not call a superclass constructor then add an annotation to that effect as a hint to the parser

The logic for a destructor would be similar but an implicit call of the superclass destructor would be after the subclass constructor is called.