Class: FFXIVLodestone::Character

Inherits:
Object
  • Object
show all
Defined in:
lib/ffxiv-lodestone.rb

Overview

Represents an FFXIV character. Example:

FFXIVLodestone::Character.new(1015990)

Defined Under Namespace

Classes: AmbiguousNameError, NotFoundException, SkillList, StatList

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ Character

Returns a new instance of Character.

Raises:

  • (ArgumentError)


167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/ffxiv-lodestone.rb', line 167

def initialize(args={})
  args = {:id => args} unless args.class == Hash
  raise ArgumentError, 'No search paremeters were given.' if args.empty?

  if args.key? :id
    character_id = args[:id]
    raise ArgumentError, 'No other arguments may be specified in conjunction with :id.' if args.size > 1
  else
    characters = Character.search(args)
    raise NotFoundException, 'Character search yielded no results.' if characters.empty?
    raise AmbiguousNameError, 'Multiple characters matched that name.' if characters.size > 1
    character_id = characters.first[:id]
  end
  
  @character_id = character_id
  doc = Nokogiri::HTML(Character.get_profile_html(@character_id))

  # Did we get an error page? Invalid ID, etc.
  if !((doc.search('head title').first.content.match /error/i) == nil)
    raise NotFoundException, 'Bad character ID or Lodestone is broken.' 
  end

  # The skills table doesn't have a unqiue ID or class to find it by, so take the first skill lable and go up two elements (table -> tr -> th.mianskill-lable)
  @skills = SkillList.new(doc.search('th.mainskill-label').first.parent.parent)
  @stats = StatList.new(doc.search("div.contents-subheader[contains('Attributes')]").first.next_sibling)
  @resistances = StatList.new(doc.search("div.contents-subheader[contains('Elements')]").first.next_sibling)
  
  # The character info box at the top ... actually has a useful ID!
  @profile = {}
  profile = doc.search("//div[starts-with(@id,'profile-plate')]") 
  profile.search('tr th').each do |th|
    key = th.content.strip.downcase.gsub(':','').gsub(' ','_').to_sym
    value = th.next_sibling.content.strip_nbsp

    # HP/MP/TP are max values. They depend on the currently equipped job and are not very
    # meaningful pieces of data. XP will be handled seperately. 
    unless [:hp, :mp, :tp, :experience_points].include? key
      @profile[key] = value 
    end

    if key == :experience_points
      @profile[:current_exp] = value.split('/')[0].to_i
      @profile[:exp_to_next_level] = value.split('/')[1].to_i
    end
  end
  
  # Fix this datatype.
  @profile[:physical_level] = @profile[:physical_level].to_i
  
  # Parse the character name/world line...
  name_line = profile.search('#charname').first.content.gsub(')','').strip.split(' (')
  @profile[:world] = name_line[1]
  @profile[:first_name] = name_line[0].split(' ')[0]
  @profile[:last_name] = name_line[0].split(' ')[1]

  # Parse the "Seeker of the Sun Female / Miqo'te" line... fun~
  race_line = profile.search('tr td').first.content.strip_nbsp.split(' / ')
  @profile[:race] = race_line.pop

  # horrible array splitting and popping trix. hidoi hidoi!
  race_line = race_line.first.split ' '
  @profile[:gender] = race_line.pop
  @profile[:clan] = race_line.join ' '

  @profile.merge! generate_portrait_urls(doc.search('div.image-mount-image img').first.attr('src').strip)

  @profile[:character_id] = @character_id.to_i
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method) ⇒ Object



288
289
290
291
# File 'lib/ffxiv-lodestone.rb', line 288

def method_missing(method)
  return @profile[method] if @profile.key? method
  super
end

Instance Attribute Details

#profileObject (readonly)

FFXIVLodestone::Character::SkillList



165
166
167
# File 'lib/ffxiv-lodestone.rb', line 165

def profile
  @profile
end

#resistancesObject (readonly)

FFXIVLodestone::Character::SkillList



165
166
167
# File 'lib/ffxiv-lodestone.rb', line 165

def resistances
  @resistances
end

#skillsObject (readonly) Also known as: jobs

FFXIVLodestone::Character::SkillList



165
166
167
# File 'lib/ffxiv-lodestone.rb', line 165

def skills
  @skills
end

#statsObject (readonly)

FFXIVLodestone::Character::SkillList



165
166
167
# File 'lib/ffxiv-lodestone.rb', line 165

def stats
  @stats
end

Class Method Details

.search(args = {}) ⇒ Object

FFXIVLodestone::Character.search(:name => ‘Character Name’, :world => ‘Server’) => Array

Raises:

  • (ArgumentError)


237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/ffxiv-lodestone.rb', line 237

def self.search(args={})
  raise ArgumentError, 'Search parameters must be hash.' unless args.class == Hash
  raise ArgumentError, ':name must be specified to use search.' unless args.key? :name

  world_id = nil
  if args.key? :world
    # :world can be passed as a string ('Figaro') or as the integer used by the search page (7).
    # This is so the library is not completely useless when new worlds are added - developers can
    # fall back to the integers until the gem is updated.
    if args[:world].class == String
      raise ArgumentError, 'Unknown world server.' unless FFXIVLodestone::SERVER_SEARCH_INDEXES.key? args[:world].downcase.to_sym 

      world_id = FFXIVLodestone::SERVER_SEARCH_INDEXES[args[:world].downcase.to_sym] 
    else
      world_id = args[:world].to_i # force it to an int to prevent any funny business.
    end
  end

  doc = Nokogiri::HTML(get_search_html(args[:name],world_id))
  results = doc.search("table.contents-table1 tr th[contains('Character Name')]")
  return [] if results.empty? # No results = no results table header. 
  
  results = results.last.parent.parent
  results.children.first.remove # discard the table headers
  
  results.children.map do |tr|
    name_element = tr.search('td:first table tr td:last a').first
    {
      :id => name_element.attr('href').gsub('/rc/character/top?cicuid=','').strip.to_i,
      :name => name_element.content.strip,
      :portrait_thumb_url => tr.search('td:first table tr td:first img').first.attr('src').strip,
      :world => tr.search('td:last').last.content.strip
    }
  end
end

Instance Method Details

#nameObject

Returns first name / last name seperated by a space.



274
275
276
# File 'lib/ffxiv-lodestone.rb', line 274

def name
  "#{@profile[:first_name]} #{@profile[:last_name]}"
end

#to_json(args = {}) ⇒ Object



278
279
280
281
282
283
284
285
286
# File 'lib/ffxiv-lodestone.rb', line 278

def to_json(args={})
  data = {}
  data.merge!(@profile)
  data[:jobs] = @skills
  data[:attributes] = @stats
  data[:resistances] = @resistances

  data.to_json
end