Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

skill timer system

I'm trying to create a simple skill timer system where every skill has its own countdown timer (say, 30 seconds) where once you use the skill, you can't use it again for that amount of time. I don't have hardly any programming experience in Ruby but I do have a pretty good understanding of langauges such as Java.

It looks like "Game_Battler 3" would be the right place for checking if the "timer" variable is <= 0. My questions are:

1. Where would I add a "timer" variable so that each skill in the array has this field?
2. How would I make all of these variables count down each second?
3. Where could I declare each skill ID's "cooldown" timer? e.g.:
$data_skills[001].timer = 30
$data_skills[002].timer = 10
$data_skills[003].timer = 45
etc...

If there is already a script out there that does this, please link me to it. It may not be compatible with my battle system, but it may at least give me an example to look at.
 
First, you can use my Adding Attributes system to set your skills timer:
viewtopic.php?f=11&t=61159

You just have to add:
Code:
class RPG::Skill

  timer = {

    skill_id => time_to_recast_in_seconds,

    skill_id => time_to_recast_in_seconds,

    skill_id => time_to_recast_in_seconds,

    skill_id => time_to_recast_in_seconds,

    # ...

  }

  timer.default = default_time_to_recast

  add_stat('timer', times)

end

Just modify the skill_id => time_to_recast_in_seconds, with your skill_id and seconds between recast. The timer.default = default_time_to_recast is the default seconds for any skills that haven't been set in the list above that line.

Now you can view those times with:
Code:
$data_skills[skill_id].timer

Secondly, make sure you have 1, not 001. 001 converts this number to a new base, which makes it a different number all together.

Finally, hopefully this will work for you (add below the code you added above):
Code:
class Game_Battler

  alias_method :seph_skilltimers_gmbtlr_init, :initialize

  def initialize

    seph_skilltimers_gmbtlr_init

    @skill_timers = {}

  end

  alias_method :seph_skilltimers_gmbtlr_se, :skill_effect

  def skill_effect(user, skill)

    user.set_skill_timer(skill.id)

    return seph_skilltimers_gmbtlr_se(user, skill)

  end

  alias_method :seph_skilltimers_gmbtlr_scu?, :skill_can_use?

  def skill_can_use?(skill_id)

    if @skill_timers.has_key?(skill_id)

      return false if Time.now < @skill_timers[skill_id] + $data_skills[skill_id].timer

    end

    return seph_skilltimers_gmbtlr_scu?(skill_id)

  end

  def set_skill_timer(skill_id)

    @skill_timers[skill_id] = Time.new

  end

end

Depending on your script mods, that should do the trick. Let me know how that works out for you.
 
Thanks for the reply. I'm a little bit confused on what all I need to do here. All I have done so far is copied the scripts you linked me to, without making any modifications to them. I also pasted the two script blocks you posted here below those other scripts, and made appropriate modifications for the skill IDs and times. The first error I got was this:

timererror.jpg


So, I modified that section of code to this:

Code:
class RPG::Skill

  times = {

    82 => 10,

    83 => 20,

    84 => 30,

    85 => 30,

    # ...

  }

  times.default = 10

  add_stat('timer', times)

end

(I changed the times and timer around, I'm not sure if this was a mistake in the code but it seemed to make the error go away.)

Now I get this error:

timererror2.jpg


Which is pointing to this line of code in the second script block you sent me:
seph_skilltimers_gmbtlr_init

Is there something else I need to do here? Sorry for not understanding very well.
 
I still get this error for the top script block:

timererror.jpg


But if I modify it to what I said before, it doesn't give that error. Is that what it's supposed to be? (If I do modify it, it doesn't work in-game. If I use a skill that has the ID of 82 like I posted in that code, I can use that skill again immediately, I tried changing the 10 to a higher number too).
 
You modified it correctly.

The code is based for a single batter. So if battler a cast skill 82, battler b can cast skill 82 as well.

Could you make a demo with your CBS, so I can see this in action? I might have just messed something up.
 
Since Sephirothspawn hasn't been on in quite a while, could someone with scripting experience look at my first post and see if they have any ideas on how to do this? The solution doesn't necessarily have to use Sephiroth's scripts. Sephiroth, if you get back on and have the time, I'm still looking for a solution to this problem and you've been quite helpful so far.

Thanks.
 
I am looking into this right now. I am completely stumped on to why this isn't working but I am sure I will laugh at myself once I figure it out.

EDIT: Knew I would laugh at myself. Anyway, here's the fixed code:
Code:
class RPG::Skill

  times = {

    7 => 2000,

    #83 => 20,

    #84 => 30,

    #85 => 30,

    # ...

  }

  times.default = 10

  add_stat('timer', times)

end

 

class Game_Battler

  alias_method :seph_skilltimers_gmbtlr_init, :initialize

  def initialize

    seph_skilltimers_gmbtlr_init

    @skill_timers = {}

  end

  alias_method :seph_skilltimers_gmbtlr_se, :skill_effect

  def skill_effect(user, skill)

    user.set_skill_timer(skill.id)

    return seph_skilltimers_gmbtlr_se(user, skill)

  end

  alias_method :seph_skilltimers_gmbtlr_scu?, :skill_can_use?

  def skill_can_use?(skill_id)

    if @skill_timers.has_key?(skill_id)

      return false if Time.now < @skill_timers[skill_id] + $data_skills[skill_id].timer

    end

    return seph_skilltimers_gmbtlr_scu?(skill_id)

  end

  def set_skill_timer(skill_id)

    @skill_timers[skill_id] = Time.new

  end

end

I also updated the adding attributes code with the code below:
Code:
#==============================================================================

# ** Adding Attributes Easy

#------------------------------------------------------------------------------

# SephirothSpawn

# Version 1.1

# 2009-02-02 (Year-Month-Day)

#------------------------------------------------------------------------------

# * Version History:

#

#   Version 1.0 -------------------------------------------------- (2009-01-21)

#    Version 1.1 ------------------------------------------------- (2009-02-02)

#     - Fix: Fixed add_stat default hash setup. Changed to object_to_string.

#------------------------------------------------------------------------------

# * Description:

#

#   This script was designed to allow you to give any object (usually data 

#   structure objects) stats, either with a set stat or a stat that grows 

#   (stat is a hash, with "level" as the key and stat value as the "value") 

#   via the Curve Generator module. If you wish to use this for a object class,

#   there must be an instance @id specified to read the stats.

#------------------------------------------------------------------------------

# * Instructions:

#

#   Place the script anywhere above Main and any scripts that use it.

#------------------------------------------------------------------------------

# * Terms & Conditions:

#

#   Copyright (C) 2009 SephirothSpawn (Timothy Hoffman)

#   Free for non-commercial & commercial use.

#   Any modifications to the system are not to be re-distributed without my

#   consent.

#==============================================================================

 

#==============================================================================

# ** Module

#==============================================================================

 

class Module

  #--------------------------------------------------------------------------

  # * Add Stat

  #--------------------------------------------------------------------------

  private

  def add_stat(stat_name, hash_settings)

    # Create method

    s  = "def #{stat_name};"

    s += "  settings = #{object_to_string(hash_settings)};"

    s += "  settings.default = #{object_to_string(hash_settings.default)};"

    s += "  return settings[@id];"

    s += "end;"

    # Eval method

    module_eval (s)

  end

  #--------------------------------------------------------------------------

  # * Add Stat Curve

  #--------------------------------------------------------------------------

  private

  def add_stat_curve(stat_name, hash_settings)

    # Create method

    s  = "def #{stat_name};"

    s += '  @gen_stats = {} if @gen_stats == nil;'

    s += "  if @gen_stats['#{stat_name}'] == nil;"

    s += "    @gen_stats['#{stat_name}'] = {};"

    s += "  end;"

    s += "  if @gen_stats['#{stat_name}'].has_key?(@id);"

    s += "    return @gen_stats['#{stat_name}'][@id];"

    s += "  end;"

    s += "  settings = #{object_to_string(hash_settings)};"

    s += "  settings.default = #{object_to_string(hash_settings.default)};"

    s += "  curve = Curve_Generator.generate_curve(*hash_settings[@id]);"

    s += "  @gen_stats['#{stat_name}'][@id] = curve;"

    s += "  return @gen_stats['#{stat_name}'][@id];"

    s += "end;"

    # Eval method

    module_eval (s)

  end

  #--------------------------------------------------------------------------

  # * Object to String

  #--------------------------------------------------------------------------

  def object_to_string(object)

    case object

    when Array

      object.collect! {|value| object_to_string(value)}

      return "[#{object.join(',')}]"

    when Hash

      s = '{'

      object.each do |key, value| 

        s += "#{object_to_string(key)}=>#{object_to_string(value)},"

      end

      s += '}'

      return s

    else

      return object.to_s

    end

  end

end
 
Thank you very much, this helps a lot and is what I was looking for. In addition to this, there are a couple of enhancements that I was wondering if we could have done:

1. Currently, if I for example use a skill, then open the skill menu again, the skill will be unuseable like it's supposed to be (and grayed out). If I wait long enough, I will be able to use the skill again, however it will remain grayed out unless I press Esc and open the Skill menu again. It would be ideal for the skill to change to its normal active color if the user is already in the skill menu once the skill becomes available.

2. Secondly, some kind of display for the current state of the skill waiting process would be nice. The simplest would be a countdown timer displayed at the top right for a particular selected skill as to how much time is left before it is ready to be used. Better than that, though, would be a "bar" indicator at the top right instead of a number, and even better yet, there would be bars for each skill shown simultaneously (like behind the skill name or something).

If these requests would be too much work, please let me know and I'll try to figure something out. If these things would be trivial for you, please consider making these enhancements for me.

Thank you!
 
1) I thought of this. It would basically require refreshing the window every frame/second. You can try adding this
[rgss]class Window_Skill
  alias_method :seph_skilltimers_wnskl_refresh, :refresh
  def refresh
    @last_refresh = Time.now
    seph_skilltimers_wnskl_refresh
  end
  alias_method :seph_skilltimers_wnskl_update, :update
  def update
    seph_skilltimers_wnskl_update
    refresh if Time.now > @last_refresh + 1
  end
end
[/rgss]

Theres a better way around this, but putting in flags for each item and redrawing single items when they can be cast again, but this should do fine.

2) Countdown timer is all I really have time for. A bar with Ruby's limits would also slow things down unless you used a very simple algrithm for bar drawing which wouldn't really match with the rest of your system. Anyways, give me a couple minutes and I'll post the code that shows the cooldown time by each skills sp cost.
 
Sorry, the code you gave me does not appear to solve the problem. I added it below RPG::Skill and Game_Battler. It does appear to make the "description" window at the top flash every time someone performs a turn, but the main skill window doesn't appear to ever get refreshed, because a skill doesn't get updated when it becomes available.
 
Yeah I figured. Here you go. This draws the cooldown.
[rgss]class RPG::Skill
  times = {
    7 => 2000,
    #83 => 20,
    #84 => 30,
    #85 => 30,
    # ...
  }
  times.default = 10
  add_stat('timer', times)
end
 
class Game_Battler
  alias_method :seph_skilltimers_gmbtlr_init, :initialize
  def initialize
    seph_skilltimers_gmbtlr_init
    @skill_timers = {}
  end
  alias_method :seph_skilltimers_gmbtlr_se, :skill_effect
  def skill_effect(user, skill)
    user.set_skill_timer(skill.id)
    return seph_skilltimers_gmbtlr_se(user, skill)
  end
  alias_method :seph_skilltimers_gmbtlr_scu?, :skill_can_use?
  def skill_can_use?(skill_id)
    if @skill_timers.has_key?(skill_id)
      return false if Time.now < @skill_timers[skill_id] + $data_skills[skill_id].timer
    end
    return seph_skilltimers_gmbtlr_scu?(skill_id)
  end
  def set_skill_timer(skill_id)
    @skill_timers[skill_id] = Time.new
  end
  def skill_time_left?(skill_id)
    if @skill_timers.has_key?(skill_id)
      return [((@skill_timers[skill_id] + $data_skills[skill_id].timer) - Time.now).to_i, 0].max
    end
    return 0
  end
end
 
class Window_Skill
  alias_method :seph_skilltimers_wnskl_refresh, :refresh
  def refresh
    @skill_timers = []
    seph_skilltimers_wnskl_refresh
  end
  alias_method :seph_skilltimers_wnskl_di, :draw_item
  def draw_item(index)
    seph_skilltimers_wnskl_di(index)
    skill = @data[index]
    @skill_timers.delete(index)
    if (t = @actor.skill_time_left?(skill.id)) > 0
      self.contents.font.size = 14
      x = 4 + index % 2 * (288 + 32)
      y = index / 2 * 32
      self.contents.draw_text(x + 232 - 96, y, 96, 14, "Cooldown: #{t}", 2)
      @skill_timers << index
      self.contents.font.size = Font.default_size
    end
  end
  alias_method :seph_skilltimers_wnskl_update, :update
  def update
    seph_skilltimers_wnskl_update
    if Graphics.frame_count % Graphics.frame_rate == 0
      @skill_timers.each {|i| draw_item(i)}
    end
  end
end
[/rgss]
 
Thank you very much, sir! So there's no easy way to get it to update correctly though? I don't really care if it's inefficient. If the flag thing is the only way and you have the time sometime, you could start me off on how to begin and then maybe I could finish it from there if it's easy enough.
 
I put the flags in there. You could change
[rgss]    if Graphics.frame_count % Graphics.frame_rate == 0
      @skill_timers.each {|i| draw_item(i)}
    end
[/rgss]
to
[rgss]    @skill_timers.each {|i| draw_item(i)}
[/rgss]

It probably shouldn't cause too much lag. I have it set to redraw every in game second (which depending on performance may be up to three) but with the mod it'll redraw every frame.
 
Hmm... let me check the demo. It might be one of your systems or once again, just performance. If your frame count goes down, it makes this less accurate. I'll get back to you in a few.


It seems to work fine for me. Unless I am misunderstanding you. In your demo in the "Skill Timing" section I have this code:
[rgss]class RPG::Skill
  times = {
    7 => 2000,
    #83 => 20,
    #84 => 30,
    #85 => 30,
    # ...
  }
  times.default = 10
  add_stat('timer', times)
end
 
class Game_Battler
  alias_method :seph_skilltimers_gmbtlr_init, :initialize
  def initialize
    seph_skilltimers_gmbtlr_init
    @skill_timers = {}
  end
  alias_method :seph_skilltimers_gmbtlr_se, :skill_effect
  def skill_effect(user, skill)
    user.set_skill_timer(skill.id)
    return seph_skilltimers_gmbtlr_se(user, skill)
  end
  alias_method :seph_skilltimers_gmbtlr_scu?, :skill_can_use?
  def skill_can_use?(skill_id)
    if @skill_timers.has_key?(skill_id)
      return false if Time.now < @skill_timers[skill_id] + $data_skills[skill_id].timer
    end
    return seph_skilltimers_gmbtlr_scu?(skill_id)
  end
  def set_skill_timer(skill_id)
    @skill_timers[skill_id] = Time.new
  end
  def skill_time_left?(skill_id)
    if @skill_timers.has_key?(skill_id)
      return [((@skill_timers[skill_id] + $data_skills[skill_id].timer) - Time.now).to_i, 0].max
    end
    return 0
  end
end
 
class Window_Skill
  alias_method :seph_skilltimers_wnskl_refresh, :refresh
  def refresh
    @skill_timers = []
    seph_skilltimers_wnskl_refresh
  end
  alias_method :seph_skilltimers_wnskl_di, :draw_item
  def draw_item(index)
    seph_skilltimers_wnskl_di(index)
    skill = @data[index]
    @skill_timers.delete(index)
    if (t = @actor.skill_time_left?(skill.id)) > 0
      self.contents.font.size = 14
      x = 4 + index % 2 * (288 + 32)
      y = index / 2 * 32
      self.contents.draw_text(x + 232 - 96, y, 96, 14, "Cooldown: #{t}", 2)
      @skill_timers << index
      self.contents.font.size = Font.default_size
    end
  end
  alias_method :seph_skilltimers_wnskl_update, :update
  def update
    seph_skilltimers_wnskl_update
    @skill_timers.each {|i| draw_item(i)}
  end
end
 
[/rgss]

The cooldown decreases by 1 at a time as it should.
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top