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.

Curve Generation

I need some help with the algorithm for the generation of parameter curves from RMXP. I want to know which is the formula RMXP uses(Or something equivalent) for automatically generating the parameter tables in the Actors Database. I know already how to do the easy ones, the quick random generation("A", "B", and so on buttons). But the other one I can't know how is calculated.
 
Thanks, I have found the generation methods in MACL, but they are a bit complicated and are not equal to RMXP ones, or at least I couldn't figure how to use them well. What MACL has is an "Early/Late" curve generator, which needs, min level, max level, min value, max value, until here it is what RMXP uses, but later it uses two more values "early", and "late", and RMXP has only one to input for determinating the grow form of a curve. I want to make a generation method equal to the RMXP one, but I still can't understand how these MACL methods are used.

Code:
#==============================================================================
# ** Modules.Curve Generator Module (4.0)         By Trickster & SephirothSpawn
#------------------------------------------------------------------------------
# * Description :
#
#   This script was designed to generate a curve of numbers given certain
#   values. As of now, their are 3 formulas for you to choose from : Point-
#   slope; Early & Late; Early / Steady / Late + Curves.
#------------------------------------------------------------------------------
# * Syntax :
#
#   Generating Curve :
#    - curve = Curve_Generator.generate_curve(type, <args>)
#
#   Type : 0 - Point Slope
#          1 - Early / Late
#          2 - Early / Steady / Late + Curves
#
#   Args : 
#
#    Type 0 : min_level, max_level, start value, inflation
#    Type 1 : min_level, max_level, min_value, max_value, early, late
#    Type 2 : min level, mid_level, max level, min value, mid_value, 
#             max value, early, late, steady, curve 1, curve 2, integer
#
#   This will return a hash : { level => value, ... }
#==============================================================================

MACL::Loaded << 'Modules.Curve_Generator'

#==============================================================================
# ** Curve_Generator
#==============================================================================

module Curve_Generator
  #--------------------------------------------------------------------------
  # * Generate Curve
  #
  #   Type : 0 - Point Slope
  #          1 - Early / Late
  #          2 - Early / Steady / Late + Curves
  #
  #   Args : 
  #
  #    Type 0 : min_level, max_level, start value, inflation
  #    Type 1 : min_level, max_level, min_value, max_value, early, late
  #    Type 2 : min level, mid_level, max level, min value, mid_value, 
  #             max value, early, late, steady, curve 1, curve 2, integer
  #--------------------------------------------------------------------------
  def self.generate_curve(type, *args)
    # Collects Saved Tables
    tables = self.load_tables
    # Check for Previously Generated Table
    if tables.has_key?(type) && tables[type].has_key?(args)
      # Return Previously Generated Table
      return tables[type][args]
    end
    # Branch Point By Type
    case type
    when 0 # Point Slope
      table = Point_Slope.generate_curve(*args)
    when 1 # Early / Late
      table = Early_Late.generate_curve(*args)
    when 2 # Early / Stead / Late + Curves
      table = ESL_Curves.generate_curve(*args)
    end
    # Set 0 Defaults
    table.default = 0
    # Saves Table Information
    self.save_table(type, args, table)
    # Return Curve Information
    return table
  end
  #-------------------------------------------------------------------------
  # * Save Table
  #-------------------------------------------------------------------------
  def self.save_table(type, args, table)
    # Collects Saved Tables
    tables = self.load_tables
    # Creates Hash of Type (If non present)
    tables[type] = {} unless tables.has_key?(type)
    # Saves Table Data
    tables[type][args] = table
    # Resaves Tables to File
    save_data(tables, 'Data/Curve Generator.rxdata')    
  end
  #-------------------------------------------------------------------------
  # * Load Tables
  #-------------------------------------------------------------------------
  def self.load_tables
    # Test For Saved Table File
    unless FileTest.exist?('Data/Curve Generator.rxdata')
      # Creates Curve Generator Rxdata File
      save_data({}, 'Data/Curve Generator.rxdata')
    end
    # Returns Saved Tables
    return load_data('Data/Curve Generator.rxdata')
  end  
  
  #============================================================================
  # ** Point Slope
  #============================================================================
  
  module Point_Slope
    #------------------------------------------------------------------------
    # * Generate Curve
    #
    #   Args : min_level, max_level, start value, inflation
    #------------------------------------------------------------------------
    def self.generate_curve(min_l, max_level, start_value, inflation)
      # Creates Table
      table = {}
      # Fills Table
      for level in min_l..max_l
        table[level] = start_value + inflation * level
      end
      # Return Table
      return table
    end
  end
  
  #============================================================================
  # ** Early Late
  #============================================================================
  
  module Early_Late
    #------------------------------------------------------------------------
    # * Generate Curve
    #------------------------------------------------------------------------
    def self.generate_curve(min_l, max_l, min_v, max_v, e = 1.0, l = 1.0)
      # Creates Table
      table = {}
      # Fills Table
      for level in min_l..max_l
        # Assigns Value
        table[i] = self.calculate_value(i, min_l.to_f, max_l.to_f,
                   min_v.to_f, max_v.to_f, e.to_f, l.to_f)
      end
      # Return Table
      return table
    end
    #------------------------------------------------------------------------
    # * Late Curve
    #------------------------------------------------------------------------
    def self.late_curve(level, min_level, max_level, min, max)
      diff = min_level - max_level
      stat = min - max
      num = stat * level ** 2 - 2 * min_level * stat * level + min_level ** 2 * 
            max - 2 * min_level * max_level * min + min * min_level ** 2
      denom = diff ** 2
      return num / denom
    end
    #------------------------------------------------------------------------
    # * Early Curve
    #------------------------------------------------------------------------
    def self.early_curve(level, min_level, max_level, min, max)
      diff = max_level - min_level
      stat = max - @min
      num = -stat * level ** 2 + 2 * max_level * stat * level + min_level ** 
            2 * max - 2 * min_level * max_level * max + min * max_level ** 2
      denom = diff ** 2
      return num / denom    
    end
    #------------------------------------------------------------------------
    # * Steady Curve
    #------------------------------------------------------------------------
    def self.steady_curve(level, min_level, max_level, min, max)
      ch_level = max_level - min_level
      ch_stat = max - min
      base = ch_stat / ch_level * level
      mod = max * min_level - min * max_level
      base2 = mod / ch_level
      return base - base2
    end
    #------------------------------------------------------------------------
    # * Calculate Value
    #------------------------------------------------------------------------
    def self.calculate_value(level, min_level, max_level, min, max, e, l)
      return min if level < min_level
      return max if max_level < level
      if e == l
        stat = self.steady_curve(level, min_level, max_level, min, max)
      else
        early_ = self.early_curve(level, min_level, max_level, min, max)
        late_ = self.late_curve(level, min_level, max_level, min, max)
        stat = (e * early_ + l * late_) / (e + l)
      end
      stat = Integer([[stat, min].max, max].min)
      return stat
    end
  end
      
  #============================================================================
  # ** ESL_Curves
  #============================================================================
  
  module ESL_Curves
    #------------------------------------------------------------------------
    # * Generate Curve
    #------------------------------------------------------------------------
    def self.generate_curve(mn_v, md_v, mx_v, mn_l, md_l, mx_l, e = 0, l = 0, 
                            s = 0, c1 = 0, c2 = 0, i = true)
      # Saves Values
      @min_value, @mid_value, @max_value = mn_v, md_v, mx_v
      @min_level, @mid_level, @max_level = mn_l, md_l, mx_l
      @early    , @late     , @steady    = e   , l   , s
      @curve1   , @curve2   , @integer   = c1  , c2  , i
      # Error Checking
      self.error_checking
      # Calculate constants
      self.calculate_constants
      # Returns Table
      return self.generate_table
    end
    #-----------------------------------------------------------------------
    # * Error Checking
    #-----------------------------------------------------------------------
    def self.error_checking
      if @late + @early + @steady + @curve1 + @curve2 == 0
        raise(StandardError, "No Influences Have Been Defined")
      elsif @min_level == @mid_level || @min_level == @max_level ||
            @mid_level == @max_level
        raise(StandardError, "Can't Use Same Level for Min, Mid, or Max Level")
      end
    end
    #-----------------------------------------------------------------------
    # * Calculate Constants
    #-----------------------------------------------------------------------
    def self.calculate_constants
      # Calculate "infi" and "inmi"
      @inmi = (@mid_value - @min_value) / (@mid_level - @min_level)
      @infi = (@max_value - @min_value) / (@max_level - @min_level)
      # Calculate "infimi"
      @infimi = (@infi - @inmi) / (@max_level - @mid_level)
    end
    #-----------------------------------------------------------------------
    # * Generate Table
    #-----------------------------------------------------------------------
    def self.generate_table
      # Create Hash table
      table = {}
      # Run Through Each Level
      self.each {|level, value| table[level] = value}
      # Return Created Table
      return table
    end
    #-----------------------------------------------------------------------
    # * Each Interator
    #-----------------------------------------------------------------------
    def self.each
      # Get Minimum level and Maximum Level
      minimum, maximum = @min_level.to_i, @max_level.to_i
      # Run Through Minimum and Maximum and Yield Level and Value
      (minimum..maximum).each {|level| yield(level, self.get_stat(level))}
    end
    #-----------------------------------------------------------------------
    # * Get Stat
    #-----------------------------------------------------------------------
    def self.get_stat(level)
      return @integer ? @min_value.to_i : @min_value if level <= @min_level
      return @integer ? @max_value.to_i : @max_value if level >= @max_level
      # Setup Total
      total = 0
      # Get Values for Every Stat if greater than 0
      total += @early  * self.early_curve(level)      if @early > 0
      total += @late   * self.late_curve(level)       if @late > 0
      total += @steady * self.steady_curve(level)     if @steady > 0
      total += @curve1 * self.early_late_curve(level) if @curve1 > 0
      total += @curve2 * self.late_early_curve(level) if @curve2 > 0
      # Get Average
      total /= @late + @early + @steady + @curve1 + @curve2
      # Limit Value
      total = level < @mid_level ? 
        [total, @mid_value].min : [total, @mid_value].max
      # Further Limit Value
      total = [[total, @min_value].max, @max_value].min
      # Return Value
      return @integer ? total.to_i : total
    end
    #-----------------------------------------------------------------------
    # * Late Curve
    #-----------------------------------------------------------------------
    def self.late_curve(level)
      # Calculate "A"
      a_num = @infimi * (3 * @min_level + @mid_level) + @inmi
      a_den = (@max_level - @min_level) * (@mid_level - @min_level)
      a = - a_num / a_den
      # Return Value
      return curve(a, level)
    end
    #-----------------------------------------------------------------------
    # * Early Curve
    #-----------------------------------------------------------------------
    def self.early_curve(level)
      # Calculate "A"
      a_num = @infimi * (2 * @max_level + @min_level + @mid_level) + @inmi
      a_den = (@max_level - @mid_level) * (@max_level - @min_level)
      a = - a_num / a_den
      # Return Value
      return curve(a, level)
    end
    #-----------------------------------------------------------------------
    # * Early Late Curve
    #-----------------------------------------------------------------------
    def self.early_late_curve(level)
      # Calculate "A"
      a = @infimi / (@max_level + @min_level - 2 * @mid_level)
      # Return Value
      return curve(a, level)
    end
    #-----------------------------------------------------------------------
    # * Late Early Curve
    #-----------------------------------------------------------------------
    def self.late_early_curve(level)
      # If Less than Mid Level
      if level < @mid_level
        # Return Late Curve for level
        return late_curve(level)
      # If Greater than Mid Level
      elsif level > @mid_level
        # Return Early Curve for Level
        return early_curve(level)
      # If at Mid Level
      elsif level == @mid_level
        # Return Mid Value
        return @mid_value
      end
    end
    #-----------------------------------------------------------------------
    # * Steady Curve
    #-----------------------------------------------------------------------
    def self.steady_curve(level)
      ch_level = @max_level - @min_level
      ch_stat = @max_value - @min_value
      base = ch_stat / ch_level * level
      mod = @max_value * @min_level - @min_level * @max_level
      base2 = mod / ch_level
      return base - base2
    end
    #-----------------------------------------------------------------------
    # * Curve
    #-----------------------------------------------------------------------
    def self.curve(a, level)
      # Calculate "B"
      b = @infimi - a * (@min_level + @mid_level + @max_level)
      # Calculate "C"
      c = @inmi - a * (@mid_level ** 2 + @min_level * @mid_level + 
          @min_level ** 2) - b * (@mid_level + @min_level)
      # Calculate "D"
      d = @min_value - (a * @min_level ** 3 + b * @min_level ** 2 + c * 
          @min_level)
      # Calculate Stat
      stat = a * level ** 3 + b * level ** 2 + c * level + d
      # Return Stat
      return stat
    end
  end
end
 
If you're talking about the Fast, Middle, Slow curves...
They are almost parabolas with a focal length of around 2.25 when using the full
range of 1..999.
The middle is just linear.

Middle:  stat = ((MAX - MIN) / 99) * Level + MIN
Slow:    stat = 0.094L**2 + 0.826L - 0.033
Fast:    stat = 0.104L**2 + 20.569L - 19.127

You could also solve for a parabola offset (1,1), and come really close

y = (x - 1)**2 / 4P + 1

where P is (98**2 / (MAX - 1)) / 4

Be Well
 
Well, I found almost the exact formula for the middle curve:

((MAX_stat-MIN_Stat)/(MAX_Level-MIN_Level))*(Level-MIN_Level)+Min_Stat

You have to round the value to the nearest whole number, but it seems to be the right formula. I assume that you would change it for the late or slow curves. I'll check into it.

Edit: I've found the complete formula, if you assume it rounds to the nearest whole number. Here it is:

Stat_Value = ((Max_Value-Min_Value)/(Max_Level-Min_Level))*(((Level-Min_Level)*(x-((Level-Min_Level)/(Max_Level-Min_Level))))+(Min_Value)

Where x is a value from 1.99999 to (something, i'll get back to you)

Apparently that last formula has something missing, because the last number is always 2, and it gets closer to 2 as you get closer to the max.
 

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