Not logged inOpenClonk Forum
Up Topic Development / Scenario & Object Development / Question: Goal updates
- - By Marky [de] Date 2016-10-08 21:40 Edited 2016-10-08 21:42
The structure of the goal controller and the goal library confuse me a little. As far as I understand it, the following happens:

1) One or more goals are created
2) In RecheckGoalTimer() the first goal creates the global effect "IntGoalCheck", and it becomes the current goal of that effect via the first timer call.
3) In the second timer call of "IntGoalCheck" the goal calls for a HUD update, then (probably) finds out that it is not fulfilled and the timer call ends.
4) Step 3 continues until the goal is fulfilled, then it picks the next goal, or ends the game if no more unfulfilled goals exist.

Question 1: The HUD update only updates the current goal, so in the worst case you could have fulfilled all the other goals without knowing it because the first goal was not fulfilled yet? I assumed so, because NotifyHUD only updates the goal that requests the update. Of course certain goals may request an update because of an external event, such as a player relaunch.

Question 2: Is there a good already existing callback that would allow a goal to update its status regularly? IsFulfilled() is only called on the current goal, if I understood everything correctly, so that is not an option.

Question 3: Is there is no such callback yet, then I have to use a separate timer in the goal. Is this the best option?

Question 4: I want the goal picture to be different for every player. Any objections against adding a player parameter to the relevant callbacks?
Parent - - By Sven2 Date 2016-10-08 23:26
1. The idea of checking only one goal was that some goal checks are computationally expensive and only one goal is displayed anyway. If you wanted to know the progress of all goals, you would open the goal menu (that was in Clonk Rage). The goal menu would call IsFulfilled on all the goals and then dusplay that. I think this method was just carried over to the OpenClonk HUD. I do not know if this optimization is still relevant. Most scenarios have just one goal anyway.

2. Why would you want to update the status if it is not displayed anywhere?

3. Yes, you can use a separate timer. But the timer would compute something that nobody can see.

4. You could, but then of course the goal controller would have to be changed.
Parent - - By Marky [de] Date 2016-10-09 07:05
3. Yes, nobody would see that, and I'll elaborate this idea more detailed in my reply to point 1. Before I started this topic I realized that I don't need that timer for my current problem, because the goal can manage HUD updates via events anyway. Still, the questions came up while I was looking at the code.

2. Good question. The display status does not need to be updated all the time, that is true. I can think of goals that fire certain events, e.g. give new knowledge or trigger a cutscene when they are 50% completed or something like that. These are very special cases though, which are usually tied to a special scenario anyway.

4. Good, I'll implement it then.

1. I assumed that this was the reason for that behaviour, yes. From my perspective there are three aspects of the goal that are currently combined in IsFulfilled() that do not necessarily go together:
* Asking the goal if it is fulfilled (from the goal controller for example). This is something I'd expect to go fast, I wouldn't expect that every call causes an expensive computation (in the worst case).
* Calculating the goal progress/status. Here is where I expect an expensive operation, and a good optimization probably is to let the goal decide itself when it performs the calculation and just save whether it is fulfilled in a local variable.
* Decision how to display the goal on the HUD (it was implied in your answer). Even in the current solution this is only marginally a part of IsFulfilled: The goal can request a HUD update by itself, and most of the display is in GetDescription(), GetShortDescription() and the other callbacks anyway. Furthermore, the majority of goals do not change their picture at all.

One more thing that I found is that the controller updates the HUD first and then asks the goal if it is fulfilled, so the displayed status can lack one step behind for some goals. I'd change that around.
Parent - - By Sven2 Date 2016-10-09 15:24 Edited 2016-10-09 15:28
A function like IsFulfilled is needed e.g. if someone clicks the goal menu and wants to see all goals and their fulfilled status. Whether the goal upodates its state via a timer, via events or keeps a cached result is up to the goal. IsFulfilled is pretty much the simplest interface you can have. It makes no assumption about how the goal achieves that and the goal does not have to know how often an update is needed.

For example, if a goal menu is open, you want an immediate update. So you call IsFulfilled more often - possibly every frame while the goal is open. If it's a league game, you may want to ask the goal more often as well (that was done e.g. in CR in the settlement league). If a sequence trigger is bound to a goal fulfilled status, it may also check the goal more often and also check inactive goals.

If the periodic call to the expensive check operation were done in the goal (i.e.: each goal that cannot work with events keeps its timer), then the goal would have to decide how often to update itself, even though it doesn't know that. If the goal were to do a timer and then inform everyone else of its change, you have a much more complex interface:
1. The refresh rate would be fixed and a goal user (such as the HUD) had no way to speed it up. If you wanted that, then each goal would have to implement something like SetCheckTimerInterval
2. Each goal would have to keep a list of potential users to callback to. You'd need to write a callback list (or hardcode all potential goal status users in the goal object - that's bad design because it would increase inter-dependencies)
3. Each goal that cannot work based on an event would have to implement its own timer.

Of course that extra logic could be hidden in Library_Goal and goals just keep either implementing IsFulfilled or have a property telling the controller "I work via events", and call SetFulfilled () (in that case, the default implementation of IsFulfilled would just check a variable defined in Library_Goal). It's a more complex interface. The upside would be that it saves having a timer for some goals (the ones that can be decided by an event).
Parent - - By Marky Date 2016-10-09 15:45

>A function like IsFulfilled is needed e.g. if someone clicks the goal menu and wants to see all goals and their fulfilled status.

Exactly. Summed up, I wanted to say something similar to what you did. For example what I would do: Leave IsFulfilled() as the interface. Add an additional mode in the library for expensive goal checks that goes like this:
If the goal check is known to be expensive the last frame when it was called is saved in a property, and the goal defines a minimum delay between checks. So the function IsFulfilled can be called many times and the goal will always return the last known status. It calculates the status again if IsFulfilled() is called after the minimum delay.
Parent - By Sven2 Date 2016-10-09 16:03
You don't need an additional mode for that. You can just offer caching as a helper function:
// Script of expensive-to-check goal
public func IsFulfilled(int for_player)
  var max_cache_age = 10; // max age in frames
  return CheckFulfilledCache(this.CheckFulfilled, for_player, max_cache_age); // for_player can be passed as nil here if the cache is not to be kept per player

private func CheckFulfilled(int for_player)
  // Expensive check function

The advantage would be that non-cached IsFulfilled calls don't have to go through the library. Plus the interface doesn't have to be changed.

But as with any optimization: Don't do it if goal checks aren't actually a bottleneck. Check the script profiler first.
Parent - By Maikel Date 2016-10-09 18:42
Just in general regarding goals and rules. If at least one of them is active I'd propose showing some symbol in the right upper corner which look like stacked rules. Then clicking on that should open a menu which shows all acitve goals and rules.

Then we can also delete the goal and rules appearance in the F-Menu.
Up Topic Development / Scenario & Object Development / Question: Goal updates

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill