class RpgLevel

Constants

CLEARED_CACHED_CURRENT_LEVEL_STATUS
VERSION

Attributes

exp[R]
min_level[R]
necessary_exps[R]

Public Class Methods

new(min_level: 1) click to toggle source
# File lib/rpg_level.rb, line 8
def initialize(min_level: 1)
  @exp = 0
  @min_level = min_level
  # Necessary exps from the @min_level
  @necessary_exps = []
  # A cache of the #generate_status_of_current_level calculation
  # It is too heavy for access to #level_status like a static value
  @cached_current_level_status = CLEARED_CACHED_CURRENT_LEVEL_STATUS
end

Private Class Methods

generate_exp_change_result(before_exp, after_exp, before_level, after_level) click to toggle source
# File lib/rpg_level.rb, line 156
def self.generate_exp_change_result(before_exp, after_exp, before_level, after_level)
  {
    before_exp: before_exp,
    after_exp: after_exp,
    exp_delta: after_exp - before_exp,
    before_level: before_level,
    after_level: after_level,
    level_delta: after_level - before_level,
    is_leveling_up: after_level > before_level,
    is_leveling_down: after_level < before_level
  }
end

Public Instance Methods

alter_exp(exp_delta) click to toggle source
# File lib/rpg_level.rb, line 77
def alter_exp(exp_delta)
  raise ArgumentError.new('exp_delta is not a integer') unless exp_delta.is_a?(Integer)
  before_exp = @exp
  change_exp_result = change_exp(@exp + exp_delta)
  self.class.generate_exp_change_result(
    before_exp, @exp, change_exp_result[:before_level], change_exp_result[:after_level])
end
calculate_total_necessary_exp(from_level, to_level) click to toggle source
# File lib/rpg_level.rb, line 50
def calculate_total_necessary_exp(from_level, to_level)
  raise ArgumentError.new('from_level is greater than to_level') if from_level > to_level
  raise ArgumentError.new('from_level is out of range') unless is_allowed_level?(from_level)
  raise ArgumentError.new('to_level is out of range') unless is_allowed_level?(to_level)

  (from_level..to_level).inject(0) do |result, level|
    result + find_necessary_exp_by_level(level)
  end
end
clear_exp() click to toggle source
# File lib/rpg_level.rb, line 95
def clear_exp
  change_exp(0)
  nil
end
define_exp_table(max_level) { |info| ... } click to toggle source
# File lib/rpg_level.rb, line 28
def define_exp_table(max_level)
  raise ArgumentError.new('max_level is less than min_level') if max_level < @min_level
  raise ArgumentError unless block_given?
  # TODO: How to convert the block to a proc?
  necessary_exps = generate_necessary_exps(max_level) {|info| yield(info)}
  define_exp_table_from_array(necessary_exps)
end
define_exp_table_from_array(necessary_exps) click to toggle source
# File lib/rpg_level.rb, line 18
def define_exp_table_from_array(necessary_exps)
  necessary_exps.each do |v|
    raise ArgumentError.new('some of necessary_exps are not a integer') unless v.is_a?(Integer)
    raise ArgumentError.new('some of necessary_exps are less than 0') if v < 0
  end

  @necessary_exps = necessary_exps
  @necessary_exps.freeze
end
drain_exp(decrease_of_exp) click to toggle source
# File lib/rpg_level.rb, line 90
def drain_exp(decrease_of_exp)
  raise ArgumentError.new('decrease_of_exp is less than 0') if decrease_of_exp < 0
  alter_exp(-decrease_of_exp)
end
drain_exp_by_level(decrease_of_level, fraction_mode) click to toggle source

fraction_mode

:omitted
  ex) Lv3(15/20) - Lv1 => Lv2( 0/10) - As a Wiz-like games
:full
  ex) Lv3(15/20) - Lv1 => Lv2(14/15) - As a Rogue-like games
# File lib/rpg_level.rb, line 135
def drain_exp_by_level(decrease_of_level, fraction_mode)
  raise ArgumentError.new('decrease_of_level is not a integer') unless decrease_of_level.is_a?(Integer)
  raise ArgumentError.new('decrease_of_level is less than 0') if decrease_of_level < 0
  raise ArgumentError.new('fraction_mode is invalid') unless [:omitted, :full].include?(fraction_mode)

  from_level = level
  to_level = cut_level_into_valid_range(from_level - decrease_of_level)

  exp_delta = case fraction_mode
  when :omitted then
    calculate_total_necessary_exp(min_level, to_level) - @exp
  when :full then
    to_level = [to_level + 1, from_level].min
    calculate_total_necessary_exp(min_level, to_level) - @exp - 1
  end

  alter_exp(exp_delta)
end
find_necessary_exp_by_level(level) click to toggle source
# File lib/rpg_level.rb, line 44
def find_necessary_exp_by_level(level)
  return nil unless is_allowed_level?(level)
  return 0 if level == @min_level
  @necessary_exps[level - @min_level - 1]
end
is_allowed_level?(level) click to toggle source
# File lib/rpg_level.rb, line 40
def is_allowed_level?(level)
  level.between?(@min_level, max_level)
end
is_reached_max_level?() click to toggle source
# File lib/rpg_level.rb, line 73
def is_reached_max_level?
  level == max_level
end
level() click to toggle source
# File lib/rpg_level.rb, line 69
def level
  level_status[:level]
end
level_status() click to toggle source
# File lib/rpg_level.rb, line 64
def level_status
  @cached_current_level_status = generate_status_of_current_level() unless @cached_current_level_status
  @cached_current_level_status.dup
end
max_exp() click to toggle source
# File lib/rpg_level.rb, line 60
def max_exp
  calculate_total_necessary_exp(@min_level, max_level)
end
max_level() click to toggle source
# File lib/rpg_level.rb, line 36
def max_level
  @min_level + @necessary_exps.length
end
obtain_exp(increase_of_exp) click to toggle source
# File lib/rpg_level.rb, line 85
def obtain_exp(increase_of_exp)
  raise ArgumentError.new('increase_of_exp is less than 0') if increase_of_exp < 0
  alter_exp(increase_of_exp)
end
obtain_exp_by_level(increase_of_level, fraction_mode) click to toggle source

fraction_mode

:omitted
  ex ) Lv3(15/20) + Lv1 => Lv4( 0/25) - As a Rogue-like games
:inherited
  ex1) Lv3(15/20) + Lv1 => Lv4(15/25)
  ex2) Lv3(15/20) + Lv1 => Lv4(14/15)
# File lib/rpg_level.rb, line 106
def obtain_exp_by_level(increase_of_level, fraction_mode)
  raise ArgumentError.new('increase_of_level is not a integer') unless increase_of_level.is_a?(Integer)
  raise ArgumentError.new('increase_of_level is less than 0') if increase_of_level < 0
  raise ArgumentError.new('fraction_mode is invalid') unless [:omitted, :inherited].include?(fraction_mode)

  from_level = level
  to_level = cut_level_into_valid_range(from_level + increase_of_level)

  exp_delta = case fraction_mode
  when :omitted then
    calculate_total_necessary_exp(min_level, to_level) - @exp
  when :inherited then
    next_necessary_exp_after_leveling_up = find_necessary_exp_by_level(to_level + 1)
    inherited_fraction_exp = if next_necessary_exp_after_leveling_up
      [level_status[:obtained_exp_for_next], next_necessary_exp_after_leveling_up - 1].min
    else
      0
    end
    calculate_total_necessary_exp(min_level, to_level) + inherited_fraction_exp - @exp
  end

  alter_exp(exp_delta)
end

Private Instance Methods

change_exp(exp) click to toggle source
# File lib/rpg_level.rb, line 226
def change_exp(exp)
  before_level = level
  @exp = cut_exp_into_valid_range(exp)
  clear_cached_current_level_status
  {
    before_level: before_level,
    after_level: level
  }
end
clear_cached_current_level_status() click to toggle source
# File lib/rpg_level.rb, line 218
def clear_cached_current_level_status
  @cached_current_level_status = CLEARED_CACHED_CURRENT_LEVEL_STATUS
end
cut_exp_into_valid_range(suggested_exp) click to toggle source
# File lib/rpg_level.rb, line 222
def cut_exp_into_valid_range(suggested_exp)
  [[suggested_exp, max_exp].min, 0].max
end
cut_level_into_valid_range(suggested_level) click to toggle source
# File lib/rpg_level.rb, line 236
def cut_level_into_valid_range(suggested_level)
  [[suggested_level, max_level].min, @min_level].max
end
generate_necessary_exps(max_level) { |{ level: level, min_level: min_level, max_level: max_level, generated_exps: generated_exps, memo: memo }| ... } click to toggle source
# File lib/rpg_level.rb, line 169
def generate_necessary_exps(max_level)
  generated_exps = []
  memo = {}

  ((@min_level + 1)..max_level).map do |level|
    exp = yield({
      level: level,
      min_level: @min_level,
      max_level: max_level,
      generated_exps: generated_exps,
      memo: memo
    })
    generated_exps << exp
    exp
  end
end
generate_status_of_current_level() click to toggle source
# File lib/rpg_level.rb, line 190
def generate_status_of_current_level
  current_level = @min_level
  next_necessary_exp = nil
  total_necessary_exp = 0
  obtained_exp_for_next = nil
  lacking_exp_for_next = nil

  @necessary_exps.each_with_index do |necessary_exp, index|
    total_necessary_exp += necessary_exp

    if @exp < total_necessary_exp
      next_necessary_exp = necessary_exp
      lacking_exp_for_next = total_necessary_exp - @exp
      obtained_exp_for_next = next_necessary_exp - lacking_exp_for_next
      break
    end

    current_level = guess_level_from_necessary_exps_index(index)
  end

  {
    level: current_level,
    next_necessary_exp: next_necessary_exp,
    lacking_exp_for_next: lacking_exp_for_next,
    obtained_exp_for_next: obtained_exp_for_next
  }
end
guess_level_from_necessary_exps_index(index) click to toggle source
# File lib/rpg_level.rb, line 186
def guess_level_from_necessary_exps_index(index)
  @min_level + index + 1
end