lib/pokepaste/pokemon.rb in pokepaste-0.0 vs lib/pokepaste/pokemon.rb in pokepaste-0.1
- old
+ new
@@ -1,9 +1,136 @@
module PokePaste
class Pokemon
- def initialize
+ attr_accessor *%i[species nickname gender item evs ivs shiny
+ ability tera_type level happiness nature moves]
+
+ @gender_abbrs = {m: :male, f: :female}
+ class << self
+ attr_accessor :gender_abbrs
end
+ def initialize(**attrs)
+ unless attrs[:species]
+ raise ArgumentError, "species required to initialize a PokePaste::Pokemon"
+ end
+
+ # Check types for things expected to be other than strings... everything else will
+ # at worst get coverted with #to_s and so will be allowed. It is not the job of
+ # this library to enforce legality, only to parse the PokéPaste format
+ [[:evs, Hash],
+ [:ivs, Hash],
+ [:level, Integer],
+ [:happiness, Integer],
+ [:moves, Array]].each do |attr, type|
+ unless attrs[attr].nil? || attrs[attr].is_a?(type)
+ raise TypeError, "expected type #{type} for :#{attr}, received #{attrs[attr].class}"
+ end
+ end
+
+ unless attrs[:shiny].nil? || [true, false].include?(attrs[:shiny])
+ raise TypeError, "expected (true|false|nil) for :shiny, received #{attrs[:shiny]}"
+ end
+
+ # Process and set defaults for EVs and IVs
+ %i[evs ivs].each do |attr|
+ # Pluck the relevant value out of the attrs Hash if there is one
+ input_attr = attrs.delete(attr) || {}
+
+ # Error if this isn't a Hash
+ unless input_attr.is_a?(Hash)
+ raise ArgumentError, "expected a Hash for :#{attr}, received #{input_attr.class}"
+ end
+
+ stats = %i[hp atk def spa spd spe]
+
+ # Error if an invalid stat is specified
+ input_attr.each do |key, value|
+ unless stats.include?(key)
+ raise ArgumentError, "invalid :#{attr} key :#{key}, expected one of #{stats.keys}"
+ end
+ end
+
+ # Set default for any stat that hasn't been specified
+ stats.each do |stat|
+ input_attr[stat] ||= attr == :evs ? 0 : 31
+ end
+
+ send "#{attr}=", input_attr
+ end
+
+ # Copy the attributes from the constructor to instance variables
+ attrs.each { |key, value| send "#{key}=", value }
+
+ # Both first letter and full word, string and symbol are supported for gender
+ if attrs[:gender]
+ gender = attrs[:gender].downcase.to_sym
+ if self.class.gender_abbrs.flatten.include?(gender)
+ # Either it's the key, or the value; this will find the value either way
+ @gender = self.class.gender_abbrs[gender] || gender
+ else
+ raise TypeError,
+ "expected nil or one of #{self.class.gender_abbrs.flatten} for :gender, received #{attrs[:gender]}"
+ end
+ end
+
+ if attrs[:nature]
+ begin
+ @nature = attrs[:nature].is_a?(Integer) ? attrs[:nature] : attrs[:nature].downcase.to_sym
+ rescue NoMethodError
+ raise TypeError,
+ "expected a (String|Symbol|Integer) for :nature, received #{attrs[:nature].class}"
+ end
+ end
+
+ @shiny ||= false
+ @level ||= 50
+ @happiness ||= 255
+ @nature ||= :hardy
+ @moves ||= []
+ end
+
def to_s
+ # Build first line
+ str = @nickname ? "#{@nickname} (#{@species})" : @species.dup
+ str << " (#{@gender.to_s[0].upcase})" if @gender
+ str << " @ #{@item}" if @item
+
+ str << "\nAbility: #{@ability}" if @ability
+ str << "\nLevel: #{@level}" if @level != 50
+ str << "\nShiny: Yes" if @shiny
+ str << "\nTera Type: #{@tera_type.to_s.capitalize}" if @tera_type
+ str << "\nEVs: #{stat_string @evs, 0}" if @evs.any? { |_, value| value != 0 }
+ # Don't print neutral natures
+ unless %i[hardy docile serious bashful quirky].include? @nature
+ str << "\n#{@nature.to_s.capitalize} Nature"
+ end
+ str << "\nIVs: #{stat_string @ivs, 31}" if @ivs.any? { |_, value| value != 31 }
+ str << "\nHappiness: #{@happiness}" if @happiness != 255
+
+ @moves.each do |move|
+ str << "\n- #{move.is_a?(Array) ? move.join(" / ") : move}"
+ end
+
+ str
+ end
+
+ def shiny?
+ @shiny
+ end
+
+ private
+
+ @@stat_inflections = {
+ hp: "HP",
+ atk: "Atk",
+ def: "Def",
+ spa: "SpA",
+ spd: "SpD",
+ spe: "Spe"
+ }
+
+ def stat_string(stats, default)
+ stats.select { |_, value| value != default}
+ .map { |stat, value| "#{value} #{@@stat_inflections[stat]}" }.join(' / ')
end
end
end