v21.1 Counter that runs always

This thread pertains to v21.1 of Pokémon Essentials.

Dracarys

Novice
Member
Joined
Aug 10, 2022
Posts
18
Hello, I'm currently writing a script that applies various effects to wild Pokémon after using certain Items, and this comes with a timer/counter. The default time of the counter is 150 seconds. I already wrote a method to decrease this counter, but now my question: How can I call this method every frame when playing, i.e., on maps, during battle, while navigating in menus, etc.? I aliased Game_Map's update and Battle::Scene's pbGraphicsUpdate method and am calling my decrease_counters method from there, but there are many other moments where the counter must decrease, e.g. when navigating in menus (Party, Summary, etc.). Do I really need to call my decrease_counters method from all possible places? I also looked for an EventHandler, but on_frame_update does also only work on the overworld.

Which class and method do I need to use so I can cover every single frame being played? In other words, how can I simulate real 150 seconds within the game?
 

Vendily

Elite Trainer
Member
Does it need to be a frame counter? it could just be a time and an offset. That's sort of how pokerus works (Script Section Overworld) in that it just saves the time of the last check and just compares the saved time with the current time to see if it should update effects
 

Dracarys

Novice
Member
Joined
Aug 10, 2022
Posts
18
That could be more difficult because of my requirements (sorry, should have mentioned earlier):
  • Display real-time multiple counters in an UI, i.e. the counters must visibly go down by 1 each second
  • I've implemented "unreal" time (and can't use Time.now because that way players could manipulate the counters by setting time on their PC) which makes calculations with pbGetTimeNow harder (although not impossible)
  • v21.1's FPS-agnostic feature must be used to ensure counters map the real time
Right now, I call this method every frame (but as mentioned, there are situations where it does not get called):

Ruby:
def get_counter(counter_type)
  @counter_start_times[counter_type] = System.uptime
end

def decrease_counters
  @counters.each do |counter_type, value|
    if !@counter_start_times[counter_type]
      get_counter(counter_type)
    elsif @counters[counter_type] > 0 && System.uptime - @counter_start_times[counter_type] >= 1.0
      @counters[counter_type] -= 1
      @counter_start_times[counter_type] = nil
    end
  end
end

Also, it seems to me that the Pokérus calculation is also in an EventHandler with on_frame_update, thus also only being called while on the overworld.
 

Vendily

Elite Trainer
Member
It's only checked on the overworld, time moves forward regardless.
The only thing that matters on this question is how often you need to check these counters. If it's real time, with System.uptime, it runs always, even if the frame itself isn't updating.

This is code I used in the inprogress update for all versions of Cable Club. This code sits in the update method. While it's not as obvious in versions that don't pause in the background (v19+), the timer continues to work in the background, and we just jump ahead to the correct number once focus comes back to the program, when we get to do the check.
Ruby:
      curtime=@timer-(Time.now-@start)
      if curtime != @total_sec
        # Calculate total number of seconds
        @total_sec = curtime
        # Make a string for displaying the timer
        min = @total_sec / 60
        sec = @total_sec % 60
        @sprites["timer"].text = _ISPRINTF("<ac>{1:02d}:{2:02d}", min, sec)
      end
View: https://twitter.com/Vendily1/status/1752746060043137179
 

Dracarys

Novice
Member
Joined
Aug 10, 2022
Posts
18
Thank you, you are right - I didn't think about that. It's sufficient if the counter is updated on the map as that is where all the effects are applied to the wild Pokémon. So I updated my code as follows:

Ruby:
def set_counter_start(counter_type)
  @counter_start_times[counter_type] = System.uptime
end

def decrease_counters
  @counters.each do |counter_type, value|
    if !@counter_start_times[counter_type]
      set_counter_start(counter_type)
    elsif @counters[counter_type] > 0
      amount = (System.uptime - @counter_start_times[counter_type]).floor
      @counters[counter_type] -= amount
      @counter_start_times[counter_type] += amount
      @counters[counter_type] = 0 if @counters[counter_type] < 0
    else
      @counters[counter_type] = 0
      @counter_start_times[counter_type] = nil
    end
  end
end
 

Dracarys

Novice
Member
Joined
Aug 10, 2022
Posts
18
Sorry for the double reply but a small addition: I replaced System.uptime with $stats.play_time. Otherwise one would run into issues with the counters after saving and loading (the counter would increase because System.uptime is 0 when loading a new play session).
 
Top