wowr 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/wowr.rb +350 -0
- data/lib/wowr/classes.rb +1299 -0
- data/lib/wowr/exceptions.rb +33 -0
- data/lib/wowr/extensions.rb +15 -0
- metadata +49 -0
data/lib/wowr.rb
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
# Wowr - Ruby library for the World of Warcraft Armory
|
2
|
+
# http://wowr.rubyforge.org/
|
3
|
+
|
4
|
+
# written by Ben Humphreys
|
5
|
+
# http://benhumphreys.co.uk/
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'lib/hpricot-0.6/hpricot'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rubygems'
|
11
|
+
require 'hpricot'
|
12
|
+
end
|
13
|
+
require 'net/http'
|
14
|
+
require 'cgi'
|
15
|
+
|
16
|
+
# TODO: what does this do?
|
17
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
18
|
+
|
19
|
+
require 'lib/exceptions'
|
20
|
+
require 'lib/extensions'
|
21
|
+
require 'lib/classes'
|
22
|
+
|
23
|
+
module Wowr
|
24
|
+
class API
|
25
|
+
|
26
|
+
@@armory_url = 'http://www.wowarmory.com/'
|
27
|
+
@@eu_armory_url = 'http://eu.wowarmory.com/'
|
28
|
+
|
29
|
+
@@character_sheet_url = 'character-sheet.xml'
|
30
|
+
@@character_talents_url = 'character-talents.xml'
|
31
|
+
@@character_skills_url = 'character-skills.xml'
|
32
|
+
@@character_reputation_url = 'character-reputation.xml'
|
33
|
+
|
34
|
+
@@guild_info_url = 'guild-info.xml'
|
35
|
+
|
36
|
+
@@search_url = 'search.xml'
|
37
|
+
|
38
|
+
@@item_info_url = 'item-info.xml'
|
39
|
+
@@item_tooltip_url = 'item-tooltip.xml'
|
40
|
+
|
41
|
+
@@icon_url = 'http://wowbench.com/images/icons/32x32/'
|
42
|
+
|
43
|
+
@@arena_team_url = 'team-info.xml'
|
44
|
+
|
45
|
+
@@max_connection_tries = 10
|
46
|
+
|
47
|
+
# Part of the rails plugin stuff?
|
48
|
+
# @@profession_icons = {
|
49
|
+
# :alchemy => "Trade_Alchemy.png",
|
50
|
+
# :blacksmithing => "Trade_BlackSmithing.png",
|
51
|
+
# :engineering => "Trade_Engineering.png",
|
52
|
+
# :enchanting => "Trade_Engraving.png",
|
53
|
+
# :jewelcrafting => "INV_Hammer_21.png",
|
54
|
+
# :herbalism => "Trade_Herbalism.png",
|
55
|
+
# :leatherworking => "Trade_LeatherWorking.png",
|
56
|
+
# :mining => "Trade_Mining.png",
|
57
|
+
# :tailoring => "Trade_Tailoring.png",
|
58
|
+
# :skinning => "INV_Weapon_ShortBlade_01.png"
|
59
|
+
# }
|
60
|
+
|
61
|
+
@@classes = {
|
62
|
+
1 => 'Warrior',
|
63
|
+
2 => 'Paladin',
|
64
|
+
3 => 'Hunter',
|
65
|
+
4 => 'Rogue',
|
66
|
+
5 => 'Priest',
|
67
|
+
#6 => 'Gold Farmer', # there is no class 6
|
68
|
+
7 => 'Shaman',
|
69
|
+
8 => 'Mage',
|
70
|
+
9 => 'Warlock',
|
71
|
+
#10 => 'Purveyor of Baked Goods', # there is no class 10
|
72
|
+
11 => 'Druid'
|
73
|
+
# 12 => 'Death Knight'?
|
74
|
+
}
|
75
|
+
|
76
|
+
@@genders = {
|
77
|
+
0 => 'Male',
|
78
|
+
1 => 'Female'
|
79
|
+
}
|
80
|
+
|
81
|
+
@@races = {
|
82
|
+
1 => 'Human',
|
83
|
+
1 => 'Orc',
|
84
|
+
3 => 'Dwarf',
|
85
|
+
4 => 'Night Elf',
|
86
|
+
5 => 'Undead',
|
87
|
+
6 => 'Tauren',
|
88
|
+
7 => 'Gnome',
|
89
|
+
8 => 'Troll',
|
90
|
+
#9 => 'Pandaren', # there is no race 9
|
91
|
+
10 => 'Blood Elf',
|
92
|
+
11 => 'Draenei'
|
93
|
+
}
|
94
|
+
|
95
|
+
@@search_types = {
|
96
|
+
#:all => 'all', # TODO: All is too complex at the moment, API doesn't return all results in one query
|
97
|
+
:item => 'items',
|
98
|
+
:character => 'characters',
|
99
|
+
:guild => 'guilds',
|
100
|
+
:arena_team => 'arenateams'
|
101
|
+
}
|
102
|
+
|
103
|
+
@@arena_team_sizes = [2, 3, 5]
|
104
|
+
|
105
|
+
# TODO: Refactor, rethink and remove locale from all requests by passing unused options on to get_xml
|
106
|
+
|
107
|
+
attr_accessor :character_name, :guild_name, :realm, :locale
|
108
|
+
|
109
|
+
# You can set up the API with an optional default guild and realm
|
110
|
+
# These will be used in all your API requests unless you specify otherwise
|
111
|
+
# For item requests, the locale will not matter in results, but may affect the speed of replies
|
112
|
+
# TODO: are these nil declarations pointless?
|
113
|
+
def initialize(options = {:character_name => nil, :guild_name => nil, :realm => nil, :locale => :us})
|
114
|
+
@character_name = options[:character_name]
|
115
|
+
@guild_name = options[:guild_name]
|
116
|
+
@realm = options[:realm]
|
117
|
+
@locale = options[:locale] #|| :us
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# General-purpose search
|
122
|
+
def search(options = {:search => nil, :type => nil})
|
123
|
+
if @@search_types.include? options[:type]
|
124
|
+
raise Wowr::Exceptions::InvalidSearchType.new
|
125
|
+
end
|
126
|
+
|
127
|
+
puts options.to_yaml
|
128
|
+
|
129
|
+
if options[:search].nil?
|
130
|
+
raise Wowr::Exceptions::NoSearchString.new
|
131
|
+
end
|
132
|
+
|
133
|
+
xml = get_xml(@@search_url, options)
|
134
|
+
|
135
|
+
puts xml
|
136
|
+
|
137
|
+
results = []
|
138
|
+
|
139
|
+
if (xml%'armorySearch'%'searchResults')
|
140
|
+
case options[:type]
|
141
|
+
|
142
|
+
# TODO: Filter stuff
|
143
|
+
when @@search_types[:item]
|
144
|
+
(xml%'armorySearch'%'searchResults'%'items'/:item).each do |item|
|
145
|
+
results << Wowr::Classes::SearchItem.new(item)
|
146
|
+
end
|
147
|
+
|
148
|
+
when @@search_types[:character]
|
149
|
+
(xml%'armorySearch'%'searchResults'%'characters'/:character).each do |char|
|
150
|
+
results << Wowr::Classes::Character.new(char)
|
151
|
+
end
|
152
|
+
|
153
|
+
when @@search_types[:guild]
|
154
|
+
(xml%'armorySearch'%'searchResults'%'guilds'/:guild).each do |guild|
|
155
|
+
results << Wowr::Classes::Guild.new(guild)
|
156
|
+
end
|
157
|
+
|
158
|
+
when @@search_types[:arena_team]
|
159
|
+
(xml%'armorySearch'%'searchResults'%'arenaTeams'/:arenaTeam).each do |team|
|
160
|
+
results << Wowr::Classes::ArenaTeam.new(team)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
return results
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
|
170
|
+
# Characters
|
171
|
+
# Note searches go across all realms by default
|
172
|
+
def search_characters(options = {:name => @character_name})
|
173
|
+
options.merge!(:type => @@search_types[:character])
|
174
|
+
return search(options)
|
175
|
+
end
|
176
|
+
|
177
|
+
def get_character_sheet(options = {:character_name => @character_name, :realm => @realm})
|
178
|
+
xml = get_xml(@@character_sheet_url, options)
|
179
|
+
return Wowr::Classes::CharacterSheet.new(xml)
|
180
|
+
end
|
181
|
+
|
182
|
+
def get_character_list(min_level, klass)
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
# ??
|
187
|
+
def get_character_skills(options = {:character_name => @character_name, :realm => @realm})
|
188
|
+
compute_hash(options.merge(:url => @@character_skills_url)) ||
|
189
|
+
process_query(Wowr::Classes::Skill, @character_skills_url, false)
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
# Guilds
|
194
|
+
def search_guilds(options = {:search => @guild_name, :locale => @locale})
|
195
|
+
options.merge!(:type => @@search_types[:guild])
|
196
|
+
return search(options)
|
197
|
+
end
|
198
|
+
|
199
|
+
def get_guild(options = {:guild_name => @guild_name, :realm => @realm})
|
200
|
+
xml = get_xml(@@guild_info_url, options)
|
201
|
+
return Wowr::Classes::Guild.new(xml)
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
# Items
|
207
|
+
def search_items(options = {:search => nil})
|
208
|
+
options.merge!(:type => @@search_types[:item])
|
209
|
+
return search(options)
|
210
|
+
end
|
211
|
+
|
212
|
+
# TODO: Is not finding the item an exception or just return nil?
|
213
|
+
def get_item(options = {:item_id => nil, :locale => @locale})
|
214
|
+
|
215
|
+
#return Wowr::Classes::ItemTooltip.new(xml%'itemTooltip')
|
216
|
+
end
|
217
|
+
|
218
|
+
def get_item_info(options = {:item_id => nil, :locale => @locale})
|
219
|
+
xml = get_xml(@@item_info_url, options)
|
220
|
+
if (xml%'itemInfo'%'item')
|
221
|
+
return Wowr::Classes::ItemInfo.new(xml%'itemInfo'%'item')
|
222
|
+
else
|
223
|
+
return nil
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def get_item_tooltip(options = {:item_id => nil})
|
228
|
+
xml = get_xml(@@item_tooltip_url, options)
|
229
|
+
|
230
|
+
# tooltip returns empty document when not found
|
231
|
+
if xml.nil?
|
232
|
+
return nil
|
233
|
+
#raise Wowr::Exceptions::ItemNotFound.new("Item not found with id: #{options[:item_id]}")
|
234
|
+
end
|
235
|
+
return Wowr::Classes::ItemTooltip.new(xml%'itemTooltip')
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
|
240
|
+
# Arena Teams
|
241
|
+
def search_arena_teams(options = {})
|
242
|
+
options.merge!(:type => @@search_types[:arena_team])
|
243
|
+
return search(options)
|
244
|
+
end
|
245
|
+
|
246
|
+
def get_arena_team(options = {:team_name => :nil, :team_size => nil, :realm => @realm})
|
247
|
+
if !@@arena_team_sizes.include?(options[:team_size])
|
248
|
+
raise Wowr::Exceptions::InvalidArenaTeamSize.new("Arena teams size must be: #{@@arena_team_sizes.inspect}")
|
249
|
+
end
|
250
|
+
|
251
|
+
xml = get_xml(@@arena_team_url, options)
|
252
|
+
return Wowr::Classes::ArenaTeam.new(xml%'arenaTeam')
|
253
|
+
end
|
254
|
+
|
255
|
+
# this is more of a rails-plugin thing
|
256
|
+
# def icon(icon_url)
|
257
|
+
# @@icon_url + icon_url
|
258
|
+
# end
|
259
|
+
|
260
|
+
|
261
|
+
protected
|
262
|
+
def base_url(locale = @locale)
|
263
|
+
if locale == :eu
|
264
|
+
@@eu_armory_url
|
265
|
+
else
|
266
|
+
@@armory_url
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# TODO: pretty damn hacky
|
271
|
+
def get_xml(url, opts = {})
|
272
|
+
|
273
|
+
# better way of doing this?
|
274
|
+
reqs = {
|
275
|
+
:character_name => 'n',
|
276
|
+
:realm => 'r',
|
277
|
+
:search => 'searchQuery',
|
278
|
+
:type => 'searchType',
|
279
|
+
:guild_name => 'n',
|
280
|
+
:item_id => 'i',
|
281
|
+
:team_size => 'ts',
|
282
|
+
:team_name => 't'
|
283
|
+
}
|
284
|
+
|
285
|
+
params = []
|
286
|
+
opts.each do |key, value|
|
287
|
+
if reqs[key]
|
288
|
+
params << "#{reqs[key]}=#{u(value)}"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
if params.size > 0
|
293
|
+
query = '?' + params.join('&')
|
294
|
+
end
|
295
|
+
|
296
|
+
locale = opts[:locale] || @locale
|
297
|
+
|
298
|
+
base = self.base_url(locale)
|
299
|
+
uri = URI.parse(base + url)
|
300
|
+
full_query = base + url + query
|
301
|
+
#puts full_query
|
302
|
+
req = Net::HTTP::Get.new(full_query)
|
303
|
+
req["user-agent"] = "Mozilla/5.0 Gecko/20070219 Firefox/2.0.0.2"
|
304
|
+
res = Net::HTTP.new(uri.host, uri.port).start {|http| http.request(req) }
|
305
|
+
|
306
|
+
tries = 0
|
307
|
+
response = case res
|
308
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
309
|
+
res.body
|
310
|
+
else
|
311
|
+
tries += 1
|
312
|
+
if tries > @@max_connection_tries
|
313
|
+
raise Wowr::Exceptions::NetworkTimeout.new('Timed out')
|
314
|
+
else
|
315
|
+
retry
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
#puts response
|
320
|
+
|
321
|
+
doc = Hpricot.XML(response)
|
322
|
+
begin
|
323
|
+
errors = doc.search("*[@errCode]")
|
324
|
+
#errors.to_yaml
|
325
|
+
if errors.size > 0
|
326
|
+
errors.each do |error|
|
327
|
+
raise Wowr::Exceptions::raise_me(error[:errCode])
|
328
|
+
end
|
329
|
+
else
|
330
|
+
return (doc%'page')
|
331
|
+
end
|
332
|
+
rescue Exception => e
|
333
|
+
$stderr.puts "Fatal error ((#{e.to_s})): Couldn't search the XML document."
|
334
|
+
$stderr.puts doc
|
335
|
+
exit 1
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
# TODO: URL encoding of query strings
|
341
|
+
def u(str)
|
342
|
+
if str.instance_of?(String)
|
343
|
+
return CGI.escape(str)
|
344
|
+
else
|
345
|
+
return str
|
346
|
+
end
|
347
|
+
#stripslashes(str)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
data/lib/wowr/classes.rb
ADDED
@@ -0,0 +1,1299 @@
|
|
1
|
+
# TODO: Item sources - Vendors
|
2
|
+
# sourceType.vendor
|
3
|
+
# sourceType.questReward
|
4
|
+
# sourceType.createdBySpell
|
5
|
+
|
6
|
+
# TODO: Split up classes depending on subject?
|
7
|
+
# Character, Item, Guild, ArenaTeam
|
8
|
+
|
9
|
+
|
10
|
+
# Wowr was written by Ben Humphreys!
|
11
|
+
# http://wowr.benhumphreys.co.uk/
|
12
|
+
module Wowr
|
13
|
+
module Classes
|
14
|
+
|
15
|
+
# Composed of an ItemInfo and
|
16
|
+
# Needs to be consolidated with ItemInfo and other stuff
|
17
|
+
# to be a parent class that they extend?
|
18
|
+
class Item
|
19
|
+
attr_reader :id, :name, :icon
|
20
|
+
alias_method :item_id, :id
|
21
|
+
|
22
|
+
def initialize(elem)
|
23
|
+
@id = elem[:id].to_i
|
24
|
+
@name = elem[:name]
|
25
|
+
@icon = elem[:icon]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# Short character info, used in guild lists etc.
|
31
|
+
class Character
|
32
|
+
attr_reader :name, :level, :url, :rank,
|
33
|
+
:klass, :klass_id,
|
34
|
+
:gender, :gender_id,
|
35
|
+
:race, :race_id,
|
36
|
+
:guild, :guild_id,
|
37
|
+
:battle_group, :last_login,
|
38
|
+
:relevance, :search_rank,
|
39
|
+
|
40
|
+
:season_games_played, :season_games_won, :team_rank # From ArenaTeam info
|
41
|
+
|
42
|
+
def initialize(elem)
|
43
|
+
@name = elem[:name]
|
44
|
+
@level = elem[:level].to_i
|
45
|
+
@url = elem[:url] || elem[:charUrl]
|
46
|
+
@rank = elem[:rank].to_i
|
47
|
+
|
48
|
+
@klass = elem[:class]
|
49
|
+
@klass_id = elem[:classId].to_i
|
50
|
+
|
51
|
+
@gender = elem[:gender]
|
52
|
+
@gender_id = elem[:genderId].to_i
|
53
|
+
|
54
|
+
@race = elem[:race]
|
55
|
+
@race_id = elem[:raceId].to_i
|
56
|
+
|
57
|
+
@guild = elem[:guild] == "" ? nil : elem[:guild]
|
58
|
+
@guild_id = elem[:guildId].to_i == 0 ? nil : elem[:guildId].to_i
|
59
|
+
|
60
|
+
@battle_group = elem[:battleGroup]
|
61
|
+
@battle_group_id = elem[:battleGroupId].to_i
|
62
|
+
|
63
|
+
@relevance = elem[:relevance].to_i
|
64
|
+
@search_rank = elem[:searchRank].to_i
|
65
|
+
|
66
|
+
@last_login = elem[:lastLoginDate] == "" ? nil : elem[:lastLoginDate]
|
67
|
+
|
68
|
+
# From ArenaTeam info, can be blank on normal requests
|
69
|
+
@season_games_played = elem[:seasonGamesPlayed].to_i
|
70
|
+
@season_games_won = elem[:seasonGamesWon].to_i
|
71
|
+
@team_rank = elem[:teamRank].to_i
|
72
|
+
#@char_url = elem[:charUrl] # TODO: Merge with URL?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
# Full character details
|
79
|
+
# uses characterInfo element
|
80
|
+
class CharacterSheet
|
81
|
+
|
82
|
+
# character_info
|
83
|
+
attr_reader :name, :level, :char_url, :title,
|
84
|
+
:gender, :gender_id,
|
85
|
+
:race, :race_id,
|
86
|
+
:klass, :klass_id,
|
87
|
+
:faction, :faction_id,
|
88
|
+
:guild_name, :guild_url,
|
89
|
+
:realm,
|
90
|
+
:battle_group,
|
91
|
+
:last_modified
|
92
|
+
|
93
|
+
# character_tab
|
94
|
+
attr_reader :health, :second_bar,
|
95
|
+
:strength, :agility, :stamina, :intellect, :spirit
|
96
|
+
alias_method :str, :strength
|
97
|
+
alias_method :agi, :agility
|
98
|
+
alias_method :sta, :stamina
|
99
|
+
alias_method :int, :intellect
|
100
|
+
alias_method :spi, :spirit
|
101
|
+
|
102
|
+
attr_reader :melee, :ranged, :spell,
|
103
|
+
:defenses, :resistances,
|
104
|
+
:talent_spec, :pvp,
|
105
|
+
:professions,
|
106
|
+
:items,
|
107
|
+
:buffs, :debuffs
|
108
|
+
|
109
|
+
# It's made up of two parts
|
110
|
+
# Don't care about battlegroups yet
|
111
|
+
# I don't think I can call stuff from the constructor?
|
112
|
+
def initialize(elem)
|
113
|
+
character_info(elem%'character')
|
114
|
+
character_tab(elem%'characterTab')
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def character_info(elem)
|
119
|
+
# basic info
|
120
|
+
@name = elem[:name]
|
121
|
+
@level = elem[:level].to_i
|
122
|
+
@char_url = elem[:url]
|
123
|
+
@rank = elem[:rank].to_i
|
124
|
+
@title = elem[:title]
|
125
|
+
|
126
|
+
@klass = elem[:class]
|
127
|
+
@klass_id = elem[:classId].to_i
|
128
|
+
|
129
|
+
@gender = elem[:gender]
|
130
|
+
@gender_id = elem[:genderId].to_i
|
131
|
+
|
132
|
+
@race = elem[:race]
|
133
|
+
@race_id = elem[:raceId].to_i
|
134
|
+
|
135
|
+
@faction = elem[:faction]
|
136
|
+
@faction_id = elem[:factionId].to_i
|
137
|
+
|
138
|
+
@guild = elem[:guild]
|
139
|
+
@guild_url = elem[:guildUrl]
|
140
|
+
|
141
|
+
@realm = elem[:realm]
|
142
|
+
|
143
|
+
@battle_group = elem[:battleGroup]
|
144
|
+
|
145
|
+
@last_modified = elem[:lastModified]#.to_time
|
146
|
+
end
|
147
|
+
|
148
|
+
def character_tab(elem)
|
149
|
+
@health = (elem%'characterBars'%'health')[:effective].to_i
|
150
|
+
@second_bar = SecondBar.new(elem%'characterBars'%'secondBar')
|
151
|
+
|
152
|
+
# base stats
|
153
|
+
# % is alias for Hpricot's 'at' method, assume only 1 baseStats/strength element
|
154
|
+
@strength = Strength.new(elem%'baseStats'%'strength')
|
155
|
+
@agility = Agility.new(elem%'baseStats'%'agility')
|
156
|
+
@stamina = Stamina.new(elem%'baseStats'%'stamina')
|
157
|
+
@intellect = Intellect.new(elem%'baseStats'%'intellect')
|
158
|
+
@spirit = Spirit.new(elem%'baseStats'%'spirit')
|
159
|
+
|
160
|
+
# damage stuff
|
161
|
+
@melee = Melee.new(elem%'melee')
|
162
|
+
@ranged = Ranged.new(elem%'ranged')
|
163
|
+
@spell = Spell.new(elem.at(' > spell')) # TODO: hacky?
|
164
|
+
@defenses = Defenses.new(elem%'defenses')
|
165
|
+
|
166
|
+
|
167
|
+
#resistances
|
168
|
+
#TODO: This keying is wrong
|
169
|
+
@resistances = []
|
170
|
+
(elem/:resistances).each do |resistance|
|
171
|
+
#@resistances[:arcane] = ArcaneResistance.new(elem[:resistances][:arcane])
|
172
|
+
#@resistances[resistance] = Resistance.new(resistance)
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
@talent_spec = TalentSpec.new(elem%'talentSpec')
|
177
|
+
|
178
|
+
@pvp = Pvp.new(elem%'pvp')
|
179
|
+
|
180
|
+
|
181
|
+
|
182
|
+
# professions
|
183
|
+
@professions = []
|
184
|
+
(elem%'professions'/:skill).each do |skill|
|
185
|
+
@professions << Profession.new(skill)
|
186
|
+
end
|
187
|
+
|
188
|
+
# items
|
189
|
+
@items = []
|
190
|
+
(elem%'items'/:item).each do |item|
|
191
|
+
@items << EquippedItem.new(item)
|
192
|
+
end
|
193
|
+
|
194
|
+
# buffs
|
195
|
+
@buffs = []
|
196
|
+
(elem%'buffs'/:spell).each do |buff|
|
197
|
+
@buffs << Buff.new(buff)
|
198
|
+
end
|
199
|
+
|
200
|
+
# debuffs
|
201
|
+
@debuffs = []
|
202
|
+
(elem%'debuffs'/:spell).each do |debuff|
|
203
|
+
@debuffs << Buff.new(debuff)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
|
210
|
+
# Note differences in values depending on class
|
211
|
+
class SecondBar
|
212
|
+
attr_reader :effective, :casting, :not_casting, :type
|
213
|
+
|
214
|
+
def initialize(elem)
|
215
|
+
@effective = elem[:effective].to_i
|
216
|
+
@casting = elem[:casting].to_i == -1 ? nil : elem[:casting].to_i
|
217
|
+
@not_casting = elem[:notCasting].to_i == -1 ? nil : elem[:notCasting].to_i
|
218
|
+
@type = elem[:type]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
|
224
|
+
class BaseStat # abstract?
|
225
|
+
attr_reader :base, :effective
|
226
|
+
end
|
227
|
+
|
228
|
+
class Strength < BaseStat
|
229
|
+
attr_reader :attack, :block
|
230
|
+
def initialize(elem)
|
231
|
+
@base = elem['base'].to_i
|
232
|
+
@effective = elem['effective'].to_i
|
233
|
+
@attack = elem['attack'].to_i
|
234
|
+
@block = elem['block'].to_i == -1 ? nil : elem['block'].to_i
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
class Agility < BaseStat
|
239
|
+
attr_reader :armor, :attack, :crit_hit_percent
|
240
|
+
def initialize(elem)
|
241
|
+
@base = elem[:base].to_i
|
242
|
+
@effective = elem[:effective].to_i
|
243
|
+
@armor = elem[:armor].to_i
|
244
|
+
@attack = elem[:attack].to_i == -1 ? nil : elem[:attack].to_i
|
245
|
+
@crit_hit_percent = elem[:critHitPercent].to_f
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class Stamina < BaseStat
|
250
|
+
attr_reader :health, :pet_bonus
|
251
|
+
def initialize(elem)
|
252
|
+
@base = elem[:base].to_i
|
253
|
+
@effective = elem[:effective].to_i
|
254
|
+
@health = elem[:health].to_i
|
255
|
+
@pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class Intellect < BaseStat
|
260
|
+
attr_reader :mana, :crit_hit_percent, :pet_bonus
|
261
|
+
def initialize(elem)
|
262
|
+
@base = elem[:base].to_i
|
263
|
+
@effective = elem[:effective].to_i
|
264
|
+
@mana = elem[:mana].to_i
|
265
|
+
@crit_hit_percent = elem[:critHitPercent].to_f
|
266
|
+
@pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
class Spirit < BaseStat
|
271
|
+
attr_reader :health_regen, :mana_regen
|
272
|
+
def initialize(elem)
|
273
|
+
@base = elem[:base].to_i
|
274
|
+
@effective = elem[:effective].to_i
|
275
|
+
@health_regen = elem[:healthRegen].to_i
|
276
|
+
@mana_regen = elem[:manaRegen].to_i
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
class Armor < BaseStat
|
281
|
+
attr_reader :percent, :pet_bonus
|
282
|
+
def initialize(elem)
|
283
|
+
@base = elem[:base].to_i
|
284
|
+
@effective = elem[:effective].to_i
|
285
|
+
@percent = elem[:percent].to_f
|
286
|
+
@pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
|
291
|
+
|
292
|
+
|
293
|
+
|
294
|
+
class Melee
|
295
|
+
attr_reader :main_hand_skill, :off_hand_skill,
|
296
|
+
:main_hand_damage, :off_hand_damage,
|
297
|
+
:main_hand_speed, :off_hand_speed,
|
298
|
+
:speed, :hit_rating, :crit_chance
|
299
|
+
|
300
|
+
def initialize(elem)
|
301
|
+
# TODO: Do these not exist anymore?
|
302
|
+
@main_hand_skill = WeaponSkill.new(elem%'mainHandWeaponSkill') if (elem%'mainHandWeaponSkill')
|
303
|
+
@off_hand_skill = WeaponSkill.new(elem%'offHandWeaponSkill') if (elem%'offHandWeaponSkill')
|
304
|
+
|
305
|
+
@main_hand_damage = WeaponDamage.new(elem%'mainHandDamage')
|
306
|
+
@off_hand_damage = WeaponDamage.new(elem%'offHandDamage')
|
307
|
+
|
308
|
+
@main_hand_speed = WeaponSpeed.new(elem%'mainHandSpeed')
|
309
|
+
@off_hand_speed = WeaponSpeed.new(elem%'offHandSpeed')
|
310
|
+
|
311
|
+
@power = WeaponPower.new(elem%'power')
|
312
|
+
@hit_rating = WeaponHitRating.new(elem%'hitRating')
|
313
|
+
@crit_chance = WeaponCritChance.new(elem%'critChance')
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
class Ranged
|
318
|
+
attr_reader :weapon_skill, :damage, :speed, :power,
|
319
|
+
:hit_rating, :crit_chance
|
320
|
+
|
321
|
+
def initialize(elem)
|
322
|
+
@weapon_skill = WeaponSkill.new(elem%'weaponSkill')
|
323
|
+
@damage = WeaponDamage.new(elem%'damage')
|
324
|
+
@speed = WeaponSpeed.new(elem%'speed')
|
325
|
+
@power = WeaponPower.new(elem%'power')
|
326
|
+
@hit_rating = WeaponHitRating.new(elem%'hitRating')
|
327
|
+
@crit_chance = WeaponCritChance.new(elem%'critChance')
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
class WeaponSkill
|
332
|
+
attr_reader :rating, :value
|
333
|
+
|
334
|
+
def initialize(elem)
|
335
|
+
@value = elem[:value].to_i == -1 ? nil : elem[:value].to_i
|
336
|
+
@rating = elem[:rating].to_i
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class WeaponDamage
|
341
|
+
attr_reader :dps, :max, :min, :percent, :speed
|
342
|
+
|
343
|
+
def initialize(elem)
|
344
|
+
@dps = elem[:dps].to_f
|
345
|
+
@max = elem[:max].to_i
|
346
|
+
@min = elem[:min].to_i
|
347
|
+
@percent = elem[:percent].to_f
|
348
|
+
@speed = elem[:speed].to_f
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
class WeaponSpeed
|
353
|
+
attr_reader :haste_percent, :haste_rating, :value
|
354
|
+
|
355
|
+
def initialize(elem)
|
356
|
+
@haste_percent = elem[:hastePercent].to_f
|
357
|
+
@haste_rating = elem[:hasteRating].to_f
|
358
|
+
@value = elem[:value].to_f
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
class WeaponPower
|
363
|
+
attr_reader :base, :effective, :increased_dps, :pet_attack, :pet_spell
|
364
|
+
|
365
|
+
def initialize(elem)
|
366
|
+
@base = elem[:base].to_i
|
367
|
+
@haste_rating = elem[:effective].to_i
|
368
|
+
@increased_dps = elem[:increasedDps].to_f
|
369
|
+
@pet_attack = (elem[:petAttack].to_f == -1 ? nil : elem[:petAttack].to_f)
|
370
|
+
@pet_spell = (elem[:petSpell].to_f == -1 ? nil : elem[:petSpell].to_f)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
class WeaponHitRating
|
375
|
+
attr_reader :increased_hit_percent, :value
|
376
|
+
|
377
|
+
def initialize(elem)
|
378
|
+
@increased_hit_percent = elem[:increasedHitPercent].to_f
|
379
|
+
@value = elem[:value].to_f
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
class WeaponCritChance
|
384
|
+
attr_reader :percent, :plus_percent, :rating
|
385
|
+
|
386
|
+
def initialize(elem)
|
387
|
+
@percent = elem[:percent].to_f
|
388
|
+
@plus_percent = elem[:plusPercent].to_f
|
389
|
+
@rating = elem[:rating].to_i
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
|
394
|
+
|
395
|
+
# Decided to do funky stuff to the XML to make it more useful.
|
396
|
+
# instead of having two seperate lists of bonusDamage and critChance
|
397
|
+
# merged it into one set of objects for each thing
|
398
|
+
class Spell
|
399
|
+
attr_reader :arcane, :fire, :frost, :holy, :nature, :shadow,
|
400
|
+
:hit_rating, :bonus_healing, :penetration, :mana_regen
|
401
|
+
|
402
|
+
def initialize(elem)
|
403
|
+
@arcane = SpellDamage.new(elem%'bonusDamage'%'arcane', elem%'critChance'%'arcane')
|
404
|
+
@fire = SpellDamage.new(elem%'bonusDamage'%'fire', elem%'critChance'%'fire')
|
405
|
+
@frost = SpellDamage.new(elem%'bonusDamage'%'frost', elem%'critChance'%'frost')
|
406
|
+
@holy = SpellDamage.new(elem%'bonusDamage'%'holy', elem%'critChance'%'holy')
|
407
|
+
@nature = SpellDamage.new(elem%'bonusDamage'%'nature', elem%'critChance'%'nature')
|
408
|
+
@shadow = SpellDamage.new(elem%'bonusDamage'%'shadow', elem%'critChance'%'shadow')
|
409
|
+
|
410
|
+
@bonus_healing = (elem%'bonusHealing')[:value].to_i # is this right??
|
411
|
+
@penetration = (elem%'penetration')[:value].to_i
|
412
|
+
@hit_rating = WeaponHitRating.new(elem%'hitRating')
|
413
|
+
@mana_regen = ManaRegen.new(elem%'manaRegen')
|
414
|
+
|
415
|
+
# elements = %w[arcane fire frost holy nature shadow]
|
416
|
+
# elements.each do |element|
|
417
|
+
# # TODO: is this a good idea?
|
418
|
+
# #instance_variable_set("@#{element}", foo) #??
|
419
|
+
# #eval("@#{element} = SpellDamage.new(elem[:bonusDamage][element][:value], elem[:critChance][element][:percent]).to_f)")
|
420
|
+
# # eval("@#{element} = SpellDamage.new((elem%'bonusDamage'%element)[:value].to_i,
|
421
|
+
# # (elem%'critChance'%element)[:percent].to_f)")
|
422
|
+
# end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
class SpellDamage
|
427
|
+
attr_reader :value, :crit_chance_percent
|
428
|
+
alias_method :percent, :crit_chance_percent
|
429
|
+
|
430
|
+
def initialize(bonusDamage_elem, critChance_elem)
|
431
|
+
@value = bonusDamage_elem[:value].to_i
|
432
|
+
@crit_chance_percent = critChance_elem[:percent].to_f
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
class ManaRegen
|
437
|
+
attr_reader :casting, :not_casting
|
438
|
+
|
439
|
+
def initialize(elem)
|
440
|
+
@casting = elem[:casting].to_f
|
441
|
+
@not_casting = elem[:notCasting].to_f
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
class PetBonus
|
446
|
+
attr_reader :attack, :damage, :from_Type
|
447
|
+
|
448
|
+
def initialize(elem)
|
449
|
+
@attack = elem[:attack].to_i == -1 ? nil : elem[:attack].to_i
|
450
|
+
@damage = elem[:damage].to_i == -1 ? nil : elem[:damage].to_i
|
451
|
+
@from_type = elem[:fromType] if elem[:fromType]
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
|
456
|
+
|
457
|
+
class Defenses
|
458
|
+
attr_reader :armor, :defense, :dodge, :parry, :block, :resilience
|
459
|
+
|
460
|
+
def initialize(elem)
|
461
|
+
@armor = Armor.new(elem%'armor')
|
462
|
+
@defense = Defense.new(elem%'defense')
|
463
|
+
@dodge = DodgeParryBlock.new(elem%'dodge')
|
464
|
+
@parry = DodgeParryBlock.new(elem%'parry')
|
465
|
+
@block = DodgeParryBlock.new(elem%'block')
|
466
|
+
@resilience = Resilience.new(elem%'resilience')
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
class Armor
|
471
|
+
attr_reader :base, :effective, :percent, :pet_bonus
|
472
|
+
|
473
|
+
def initialize(elem)
|
474
|
+
@base = elem[:base].to_i
|
475
|
+
@effective = elem[:effective].to_i
|
476
|
+
@percent = elem[:percent].to_f
|
477
|
+
@pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
class Defense
|
482
|
+
attr_reader :value, :increase_percent, :decrease_percent, :plus_defense, :rating
|
483
|
+
|
484
|
+
def initialize(elem)
|
485
|
+
@value = elem[:value].to_i
|
486
|
+
@increase_percent = elem[:increasePercent].to_f
|
487
|
+
@decrease_percent = elem[:decreasePercent].to_f
|
488
|
+
@plus_defense = elem[:plusDefense].to_i
|
489
|
+
@rating = elem[:rating].to_i
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
class DodgeParryBlock
|
494
|
+
attr_reader :percent, :increase_percent, :rating
|
495
|
+
|
496
|
+
def initialize(elem)
|
497
|
+
@percent = elem[:percent].to_f
|
498
|
+
@increase_percent = elem[:increasePercent].to_f
|
499
|
+
@rating = elem[:rating].to_i
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
class Resilience
|
504
|
+
attr_reader :damage_percent, :hit_percent, :value
|
505
|
+
|
506
|
+
def initialize(elem)
|
507
|
+
@damage_percent = elem[:damagePercent].to_f
|
508
|
+
@hit_percent = elem[:hitPercent].to_f
|
509
|
+
@value = elem[:value].to_f
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
|
514
|
+
|
515
|
+
class Resistance
|
516
|
+
attr_reader :value, :pet_bonus
|
517
|
+
|
518
|
+
def initialize(elem)
|
519
|
+
@value = elem[:value].to_i
|
520
|
+
@pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
|
525
|
+
# TODO: Array starting at 1? Not sure if this is such a hot idea
|
526
|
+
class TalentSpec
|
527
|
+
attr_reader :trees
|
528
|
+
|
529
|
+
def initialize(elem)
|
530
|
+
@trees = []
|
531
|
+
@trees[1] = elem[:treeOne].to_i
|
532
|
+
@trees[2] = elem[:treeTwo].to_i
|
533
|
+
@trees[3] = elem[:treeThree].to_i
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
|
538
|
+
# Player-versus-player data
|
539
|
+
class Pvp
|
540
|
+
attr_reader :lifetime_honorable_kills, :arena_currency
|
541
|
+
|
542
|
+
def initialize(elem)
|
543
|
+
@lifetime_honorable_kills = (elem%'lifetimehonorablekills')[:value].to_i
|
544
|
+
@arena_currency = (elem%'arenacurrency')[:value].to_i
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
|
549
|
+
# A buff
|
550
|
+
class Buff
|
551
|
+
attr_reader :name, :effect, :icon
|
552
|
+
|
553
|
+
def initialize(elem)
|
554
|
+
@name = elem[:name]
|
555
|
+
@effect = elem[:effect]
|
556
|
+
@icon = elem[:icon]
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
|
561
|
+
# A player's profession, players can only have 2 max
|
562
|
+
class Profession
|
563
|
+
attr_reader :key, :name, :max, :value
|
564
|
+
|
565
|
+
def initialize(elem)
|
566
|
+
@key = elem[:key]
|
567
|
+
@name = elem[:name]
|
568
|
+
@value = elem[:value].to_i
|
569
|
+
@max = elem[:max].to_i
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
|
574
|
+
# An item equipped to a player
|
575
|
+
class EquippedItem < Item
|
576
|
+
attr_reader :durability, :max_durability, #:id, :item_id, :icon,
|
577
|
+
:gems, :permanent_enchant,
|
578
|
+
:random_properties_id, :seed, :slot
|
579
|
+
|
580
|
+
def initialize(elem)
|
581
|
+
super(elem)
|
582
|
+
#@id = elem[:id].to_i
|
583
|
+
#@icon = elem[:icon]
|
584
|
+
@durability = elem[:durability].to_i
|
585
|
+
@max_durability = elem[:maxDurability].to_i
|
586
|
+
@gems = []
|
587
|
+
@gems[0] = elem[:gem0Id].to_i == 0 ? nil : elem[:gem0Id].to_i
|
588
|
+
@gems[1] = elem[:gem1Id].to_i == 0 ? nil : elem[:gem1Id].to_i
|
589
|
+
@gems[2] = elem[:gem2Id].to_i == 0 ? nil : elem[:gem2Id].to_i
|
590
|
+
@permanent_enchant = elem[:permanentEnchant].to_i
|
591
|
+
@random_properties_id = elem[:randomPropertiesId] == 0 ? nil : elem[:randomPropertiesId].to_i
|
592
|
+
@seed = elem[:seed].to_i # not sure if seed is so big it's overloading
|
593
|
+
@slot = elem[:slot].to_i
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
|
598
|
+
|
599
|
+
# class Damage
|
600
|
+
# :type, :min, :max, :speed, :dps
|
601
|
+
# end
|
602
|
+
#
|
603
|
+
# class ItemSource
|
604
|
+
# :area_id
|
605
|
+
# :area_name
|
606
|
+
# :creature_id
|
607
|
+
# :creature_name
|
608
|
+
# :difficulty
|
609
|
+
# :drop_rate
|
610
|
+
# :value
|
611
|
+
# end
|
612
|
+
|
613
|
+
# A player guild containing members
|
614
|
+
# note not all of these will be filled out, depending on how the data is found
|
615
|
+
|
616
|
+
class Guild
|
617
|
+
attr_reader :name, :url, :realm, :realm_url, :battle_group,
|
618
|
+
:roster_url, :stats_url, :stats_url_escape,
|
619
|
+
:faction, :faction_id,
|
620
|
+
:members, :member_count
|
621
|
+
|
622
|
+
def initialize(elem)
|
623
|
+
if (elem%'guildKey')
|
624
|
+
guild = (elem%'guildKey')
|
625
|
+
else
|
626
|
+
guild = elem
|
627
|
+
end
|
628
|
+
|
629
|
+
@name = guild[:name]
|
630
|
+
@name_url = guild[:nameUrl]
|
631
|
+
@url = guild[:url]
|
632
|
+
@realm = guild[:realm]
|
633
|
+
@realm_url = guild[:realmUrl]
|
634
|
+
@battle_group = guild[:battleGroup]
|
635
|
+
@faction = guild[:faction]
|
636
|
+
@faction_id = guild[:factionId]
|
637
|
+
|
638
|
+
# some shortened versions
|
639
|
+
if (elem%'guildInfo')
|
640
|
+
@member_count = (elem%'guildInfo'%'guild'%'members')[:memberCount].to_i || nil
|
641
|
+
|
642
|
+
@members = []
|
643
|
+
(elem%'guildInfo'%'guild'%'members'/:character).each do |char|
|
644
|
+
members << Character.new(char)
|
645
|
+
end
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
|
651
|
+
# class Guild
|
652
|
+
# attr_reader :name, :realm, :battle_group,
|
653
|
+
# :roster_url, :stats_url, :stats_url_escape,
|
654
|
+
# :members, :member_count
|
655
|
+
#
|
656
|
+
# def initialize(elem)
|
657
|
+
# @name = elem[:name]
|
658
|
+
# @realm = elem[:realm]
|
659
|
+
# @battle_group = elem[:battleGroup]
|
660
|
+
# @roster_url = elem[:rosterUrl]
|
661
|
+
# @stats_url = elem[:statsUrl]
|
662
|
+
# @stats_url_escape = elem[:statsUrlEscape]
|
663
|
+
# @member_count = (elem%'members')[:memberCount].to_i
|
664
|
+
#
|
665
|
+
# @members = []
|
666
|
+
# (elem%'members'/:character).each do |char|
|
667
|
+
# members << Character.new(char)
|
668
|
+
# end
|
669
|
+
# end
|
670
|
+
# end
|
671
|
+
|
672
|
+
|
673
|
+
|
674
|
+
|
675
|
+
|
676
|
+
# General skill category
|
677
|
+
# eg Weapon Skills, Languages
|
678
|
+
class SkillCategory
|
679
|
+
attr_reader :key, :name, :skills
|
680
|
+
|
681
|
+
def initialize(elem)
|
682
|
+
@key = elem[:key]
|
683
|
+
@name = elem[:name]
|
684
|
+
|
685
|
+
@skills = []
|
686
|
+
(elem/:skill).each do |skill|
|
687
|
+
@skills << Skill.new(skill)
|
688
|
+
end
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
#
|
693
|
+
# eg Daggers, Riding, Fishing
|
694
|
+
class Skill
|
695
|
+
attr_reader :key, :name, :value, :max
|
696
|
+
|
697
|
+
def initialize(elem)
|
698
|
+
@key = elem[:key]
|
699
|
+
@name = elem[:name]
|
700
|
+
@value = elem[:value]
|
701
|
+
@max = elem[:max]
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
|
706
|
+
|
707
|
+
|
708
|
+
# Larger group of factions
|
709
|
+
# eg Alliance, Shattrath City, Steamwheedle Cartel
|
710
|
+
# character-reputation.xml
|
711
|
+
class FactionCategory
|
712
|
+
attr_reader :key, :name, :factions
|
713
|
+
|
714
|
+
def initialize(elem)
|
715
|
+
@key = elem[:key]
|
716
|
+
@name = elem[:name]
|
717
|
+
|
718
|
+
@factions = []
|
719
|
+
(elem/:faction).each do |faction|
|
720
|
+
@factions << Faction.new(faction)
|
721
|
+
end
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
# Smaller NPC faction that is part of a FactionCategory
|
726
|
+
# eg Darnassus, Argent Dawn
|
727
|
+
class Faction
|
728
|
+
attr_reader :key, :name, :reputation
|
729
|
+
|
730
|
+
def initialize(elem)
|
731
|
+
@key = elem[:key]
|
732
|
+
@name = elem[:name]
|
733
|
+
@reputation = elem[:reputation].to_i
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
|
738
|
+
|
739
|
+
|
740
|
+
# Provides detailed item information
|
741
|
+
# Reflects information found within item-tooltip.xml
|
742
|
+
class ItemTooltip < Item
|
743
|
+
attr_reader :desc, :overall_quality_id, :bonding, :max_count, #:id, :name, :icon,
|
744
|
+
:class_id, :bonuses, :item_source,
|
745
|
+
:bonuses, :resistances,
|
746
|
+
:required_level,
|
747
|
+
:allowable_classes,
|
748
|
+
:armor, :durability,
|
749
|
+
:sockets, :socket_match_enchant,
|
750
|
+
:gem_properties
|
751
|
+
alias_method :description, :desc
|
752
|
+
|
753
|
+
def initialize(elem)
|
754
|
+
@id = (elem%'id').html.to_i
|
755
|
+
@name = (elem%'name').html
|
756
|
+
@icon = (elem%'icon').html
|
757
|
+
@desc = (elem%'desc').html if (elem%'desc')
|
758
|
+
@overall_quality_id = (elem%'overallQualityId').html.to_i
|
759
|
+
@bonding = (elem%'bonding').html.to_i
|
760
|
+
@stackable = (elem%'stackable').html.to_i if (elem%'stackable')
|
761
|
+
@max_count = (elem%'maxCount').html.to_i if (elem%'maxCount')
|
762
|
+
@class_id = (elem%'classId').html.to_i
|
763
|
+
@required_level = (elem%'requiredLevel').html.to_i if (elem%'requiredLevel')
|
764
|
+
|
765
|
+
@equipData = ItemEquipData.new(elem%'equipData')
|
766
|
+
|
767
|
+
# TODO: This appears to be a plain string at the moment
|
768
|
+
#<gemProperties>+26 Healing +9 Spell Damage and 2% Reduced Threat</gemProperties>
|
769
|
+
@gem_properties = (elem%'gemProperties').html if (elem%'gemProperties')
|
770
|
+
|
771
|
+
# not all items have damage data
|
772
|
+
@damage = ItemDamageData.new(elem%'damageData') if !(elem%'damageData').html.empty?
|
773
|
+
|
774
|
+
|
775
|
+
# TODO: Test socket data with a variety of items
|
776
|
+
# TODO: replace with socket Class?
|
777
|
+
if (elem%'socketData')
|
778
|
+
@sockets = []
|
779
|
+
(elem%'socketData'/:socket).each do |socket|
|
780
|
+
@sockets << socket[:color]
|
781
|
+
end
|
782
|
+
|
783
|
+
@socket_match_enchant = (elem%'socketData'%'socketMatchEnchant')
|
784
|
+
end
|
785
|
+
|
786
|
+
|
787
|
+
# When there is no data, stats are not present in @bonuses
|
788
|
+
# TODO: When there is no stats at all, @bonuses shouldn't be set
|
789
|
+
@bonuses = {}
|
790
|
+
|
791
|
+
bonus_stats = {
|
792
|
+
:strength => :bonusStrength,
|
793
|
+
:agility => :bonusAgility,
|
794
|
+
:stamina => :bonusStamina,
|
795
|
+
:intellect => :bonusIntellect,
|
796
|
+
:spirit => :bonusSpirit
|
797
|
+
}
|
798
|
+
bonus_stats.each do |stat, xml_elem|
|
799
|
+
@bonuses[stat] = test_stat(elem/xml_elem) if test_stat(elem/xml_elem)
|
800
|
+
end
|
801
|
+
|
802
|
+
# Resistances
|
803
|
+
@resistances = {}
|
804
|
+
|
805
|
+
resist_stats = {
|
806
|
+
:arcane => :arcaneResist,
|
807
|
+
:fire => :fireResist,
|
808
|
+
:frost => :frostResist,
|
809
|
+
:holy => :holyResist,
|
810
|
+
:nature => :natureResist,
|
811
|
+
:shadow => :shadowResist
|
812
|
+
}
|
813
|
+
resist_stats.each do |stat, xml_elem|
|
814
|
+
@resistances[stat] = test_stat(elem/xml_elem) if test_stat(elem/xml_elem)
|
815
|
+
end
|
816
|
+
|
817
|
+
|
818
|
+
#@bonuses[:strength] = (elem/:bonusStrength).html.to_i if (elem/:bonusStrength)
|
819
|
+
|
820
|
+
if (elem%'allowableClasses')
|
821
|
+
@allowable_classes = []
|
822
|
+
(elem%'allowableClasses'/:class).each do |klass|
|
823
|
+
@allowable_classes << klass.html
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
# NOTE not representing armor bonus
|
828
|
+
@armor = (elem%'armor').html.to_i if (elem%'armor')
|
829
|
+
|
830
|
+
# NOTE not representing max
|
831
|
+
@durability = (elem%'durability')[:current].to_i if (elem%'durability')
|
832
|
+
|
833
|
+
if (elem%'spellData')
|
834
|
+
@spells = []
|
835
|
+
(elem%'spellData'/:spell).each do |spell|
|
836
|
+
@spells << ItemSpell.new(spell)
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
@setData = ItemSetData.new(elem%'setData') if (elem%'setData')
|
841
|
+
|
842
|
+
# @item_sources = []
|
843
|
+
# (elem/:itemSource).each do |source|
|
844
|
+
# @item_sources << ItemSource.new(source)
|
845
|
+
# end
|
846
|
+
@item_source = ItemSource.new(elem%'itemSource') if (elem%'itemSource') # TODO: More than once source?
|
847
|
+
end
|
848
|
+
|
849
|
+
private
|
850
|
+
def test_stat(elem)
|
851
|
+
if elem
|
852
|
+
if !elem.html.empty?
|
853
|
+
return elem.html.to_i
|
854
|
+
end
|
855
|
+
end
|
856
|
+
return nil
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
class ItemEquipData
|
861
|
+
attr_reader :inventory_type, :subclass_name, :container_slots
|
862
|
+
|
863
|
+
def initialize(elem)
|
864
|
+
@inventory_type = (elem%'inventoryType').html.to_i
|
865
|
+
@subclass_name = (elem%'subclassName').html if (elem%'subclassName')
|
866
|
+
@container_slots = (elem%'containerSlots').html.to_i if (elem%'containerSlots') # for baggies
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
class ItemSetData
|
871
|
+
attr_reader :name, :items, :set_bonuses
|
872
|
+
|
873
|
+
def initialize(elem)
|
874
|
+
@name = elem[:name]
|
875
|
+
|
876
|
+
@items = []
|
877
|
+
(elem/:item).each do |item|
|
878
|
+
@items << item[:name]
|
879
|
+
end
|
880
|
+
|
881
|
+
@set_bonuses = []
|
882
|
+
(elem/:setBonus).each do |bonus|
|
883
|
+
@set_bonuses << ItemSetBonus.new(bonus)
|
884
|
+
end
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
888
|
+
class ItemSetBonus
|
889
|
+
attr_reader :threshold, :description
|
890
|
+
alias_method :desc, :description
|
891
|
+
|
892
|
+
def initialize(elem)
|
893
|
+
@threshold = elem[:threshold].to_i
|
894
|
+
@description = elem[:desc]
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
898
|
+
class ItemSpell
|
899
|
+
attr_reader :trigger, :description
|
900
|
+
alias_method :desc, :description
|
901
|
+
|
902
|
+
def initialize(elem)
|
903
|
+
@trigger = (elem%'trigger').html.to_i
|
904
|
+
@description = (elem%'desc').html
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
|
909
|
+
class ItemDamageData
|
910
|
+
attr_reader :type, :min, :max, :speed, :dps
|
911
|
+
|
912
|
+
def initialize(elem)
|
913
|
+
@type = (elem%'damage'%'type').html.to_i
|
914
|
+
@min = (elem%'damage'%'min').html.to_i
|
915
|
+
@max = (elem%'damage'%'max').html.to_i
|
916
|
+
@speed = (elem%'speed').html.to_i
|
917
|
+
@dps = (elem%'dps').html.to_f
|
918
|
+
end
|
919
|
+
end
|
920
|
+
|
921
|
+
class ItemSource
|
922
|
+
attr_reader :value,
|
923
|
+
:area_id, :area_name,
|
924
|
+
:creature_id, :creature_name,
|
925
|
+
:difficulty, :drop_rate
|
926
|
+
|
927
|
+
def initialize(elem)
|
928
|
+
@value = elem[:value]
|
929
|
+
@area_id = elem[:areaId].to_i if elem[:areaId]
|
930
|
+
@area_name = elem[:areaName] if elem[:areaName]
|
931
|
+
@creature_id = elem[:creatureId].to_i if elem[:creatureId]
|
932
|
+
@creature_name = elem[:creatureName] if elem[:creatureName]
|
933
|
+
@difficulty = elem[:difficulty] if elem[:difficulty]
|
934
|
+
@drop_rate = elem[:dropRate].to_i if elem[:dropRate]
|
935
|
+
@required_level = elem[:reqLvl].to_i if elem[:reqLvl]
|
936
|
+
end
|
937
|
+
end
|
938
|
+
|
939
|
+
|
940
|
+
|
941
|
+
# A really basic item type returned by searches
|
942
|
+
class SearchItem < Item
|
943
|
+
attr_reader :url, :rarity,
|
944
|
+
:source, :item_level, :relevance
|
945
|
+
alias_method :level, :item_level
|
946
|
+
|
947
|
+
def initialize(elem)
|
948
|
+
super(elem)
|
949
|
+
@rarity = elem[:rarity].to_i
|
950
|
+
@url = elem[:url]
|
951
|
+
|
952
|
+
@item_level = elem.at("filter[@name='itemLevel']")[:value].to_i
|
953
|
+
@source = elem.at("filter[@name='source']")[:value]
|
954
|
+
@relevance = elem.at("filter[@name='relevance']")[:value].to_i
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
|
959
|
+
|
960
|
+
|
961
|
+
# uses item-info.xml
|
962
|
+
class ItemInfo < Item
|
963
|
+
attr_reader :icon, :id, :level, :name, :quality, :type,
|
964
|
+
:cost, :disenchants, :disenchant_skill_rank, :vendors,
|
965
|
+
:plans_for
|
966
|
+
|
967
|
+
def initialize(elem)
|
968
|
+
@id = elem[:id].to_i
|
969
|
+
@level = elem[:level].to_i
|
970
|
+
@name = elem[:name]
|
971
|
+
@icon = elem[:icon]
|
972
|
+
@quality = elem[:quality].to_i
|
973
|
+
@type = elem[:type]
|
974
|
+
|
975
|
+
# Cost can be in gold, or tokens
|
976
|
+
@cost = ItemCost.new(elem%'cost') if (elem%'cost')
|
977
|
+
|
978
|
+
|
979
|
+
|
980
|
+
# is costs really an array?
|
981
|
+
#@costs = []
|
982
|
+
#(elem/:cost).each do |cost|
|
983
|
+
# @costs << ItemCost.new(cost)
|
984
|
+
#end
|
985
|
+
|
986
|
+
if (elem%'disenchantLoot')
|
987
|
+
@disenchant_skill_rank = (elem%'disenchantLoot')[:requiredSkillRank].to_i
|
988
|
+
|
989
|
+
@disenchant_items = []
|
990
|
+
(elem%'disenchantLoot'/:item).each do |item|
|
991
|
+
@disenchant_items << DisenchantItem.new(item)
|
992
|
+
end
|
993
|
+
end
|
994
|
+
|
995
|
+
if (elem%'objectiveOfQuests')
|
996
|
+
@objective_of_quests = []
|
997
|
+
(elem%'objectiveOfQuests'/:quest).each do |quest|
|
998
|
+
@objective_of_quests << ItemQuest.new(quest)
|
999
|
+
end
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
if (elem%'rewardFromQuests')
|
1003
|
+
@reward_from_quests = []
|
1004
|
+
(elem%'rewardFromQuests'/:quest).each do |quest|
|
1005
|
+
@reward_from_quests << ItemQuest.new(quest)
|
1006
|
+
end
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
if (elem%'vendors')
|
1010
|
+
@vendors = []
|
1011
|
+
(elem%'vendors'/:creature).each do |vendor|
|
1012
|
+
@vendors << ItemVendor.new(vendor)
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
if (elem%'dropCreatures')
|
1017
|
+
@drop_creatures = []
|
1018
|
+
(elem%'dropCreatures'/:creature).each do |creature|
|
1019
|
+
@drop_creatures << ItemDropCreature.new(creature)
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
if (elem%'plansFor')
|
1024
|
+
@plans_for = []
|
1025
|
+
(elem%'plansFor'/:spell).each do |plan|
|
1026
|
+
@plans_for << ItemPlansFor.new(plan)
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
if (elem%'createdBy')
|
1031
|
+
@created_by = []
|
1032
|
+
(elem%'createdBy'/:spell).each do |c|
|
1033
|
+
@created_by << ItemCreatedBy.new(c)
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
# <rewardFromQuests>
|
1040
|
+
# <quest name="Justice Dispensed" level="39" reqMinLevel="30" id="11206" area="Dustwallow Marsh" suggestedPartySize="0"></quest>
|
1041
|
+
# <quest name="Peace at Last" level="39" reqMinLevel="30" id="11152" area="Dustwallow Marsh" suggestedPartySize="0"></quest>
|
1042
|
+
# </rewardFromQuests>
|
1043
|
+
# TODO: Rename
|
1044
|
+
class ItemQuest
|
1045
|
+
attr_reader :name, :id, :level, :min_level, :area, :suggested_party_size
|
1046
|
+
|
1047
|
+
def initialize(elem)
|
1048
|
+
@name = elem[:name]
|
1049
|
+
@id = elem[:id].to_i
|
1050
|
+
@level = elem[:level].to_i
|
1051
|
+
@min_level = elem[:min_level].to_i
|
1052
|
+
@area = elem[:area]
|
1053
|
+
@suggested_party_size = elem[:suggested_party_size].to_i
|
1054
|
+
end
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
|
1058
|
+
|
1059
|
+
# Creatures that drop the item
|
1060
|
+
# <creature name="Giant Marsh Frog" minLevel="1" type="Critter" maxLevel="1" dropRate="6" id="23979" classification="0" area="Dustwallow Marsh"></creature>
|
1061
|
+
# <creature name="Nalorakk" minLevel="73" title="Bear Avatar" url="fl[source]=dungeon&fl[difficulty]=normal&fl[boss]=23576" type="Humanoid" maxLevel="73" dropRate="2" id="23576" classification="3" areaUrl="fl[source]=dungeon&fl[boss]=all&fl[difficulty]=normal&fl[dungeon]=3805" area="Zul'Aman"></creature>
|
1062
|
+
class ItemDropCreature
|
1063
|
+
attr_reader :name, :id, :type, :min_level, :max_level, :drop_rate, :classification, :area
|
1064
|
+
|
1065
|
+
def initialize(elem)
|
1066
|
+
@name = elem[:name]
|
1067
|
+
@id = elem[:id].to_i
|
1068
|
+
@min_level = elem[:minLevel].to_i
|
1069
|
+
@max_level = elem[:maxLevel].to_i
|
1070
|
+
@drop_rate = elem[:dropRate].to_i
|
1071
|
+
@classification = elem[:classification].to_i
|
1072
|
+
@area = elem[:area]
|
1073
|
+
|
1074
|
+
# optional boss stuff
|
1075
|
+
@title = elem[:title] if elem[:title] # TODO: not nil when no property?
|
1076
|
+
@url = elem[:url] if elem[:url]
|
1077
|
+
@type = elem[:type] if elem[:type] # Humanoid etc.
|
1078
|
+
@area_url = elem[:areaUrl] if elem[:areaUrl]
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
|
1083
|
+
# <cost sellPrice="280" buyPrice="5600"></cost>
|
1084
|
+
# <cost>
|
1085
|
+
# <token icon="spell_holy_championsbond" id="29434" count="60"></token>
|
1086
|
+
# </cost>
|
1087
|
+
class ItemCost
|
1088
|
+
attr_reader :buy_price, :sell_price, :tokens
|
1089
|
+
|
1090
|
+
def initialize(elem)
|
1091
|
+
@buy_price = elem[:buyPrice].to_i if elem[:buyPrice]
|
1092
|
+
@sell_price = elem[:sellPrice].to_i if elem[:sellPrice]
|
1093
|
+
|
1094
|
+
if (elem%'token')
|
1095
|
+
@tokens = []
|
1096
|
+
(elem/:token).each do |token|
|
1097
|
+
@tokens << ItemCostToken.new(token)
|
1098
|
+
end
|
1099
|
+
end
|
1100
|
+
end
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
# <token icon="spell_holy_championsbond" id="29434" count="60"></token>
|
1104
|
+
class ItemCostToken
|
1105
|
+
attr_reader :id, :icon, :count
|
1106
|
+
|
1107
|
+
def initialize(elem)
|
1108
|
+
@id = elem[:id].to_i
|
1109
|
+
@icon = elem[:icon]
|
1110
|
+
@count = elem[:count].to_i
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
# <item name="Void Crystal" minCount="1" maxCount="2" icon="inv_enchant_voidcrystal" type="Enchanting" level="70" dropRate="6" id="22450" quality="4"></item>
|
1115
|
+
class DisenchantItem
|
1116
|
+
attr_reader :name, :id, :icon, :level, :type, :drop_rate, :min_count, :max_count, :quality
|
1117
|
+
|
1118
|
+
def initialize(elem)
|
1119
|
+
@name = elem[:name]
|
1120
|
+
@id = elem[:id].to_i
|
1121
|
+
@icon = elem[:icon]
|
1122
|
+
@level = elem[:level].to_i
|
1123
|
+
@type = elem[:type]
|
1124
|
+
@drop_rate = elem[:dropRate].to_i
|
1125
|
+
@min_count = elem[:minCount].to_i
|
1126
|
+
@max_count = elem[:maxCount].to_i
|
1127
|
+
@quality = elem[:quality].to_i
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
class ItemVendor
|
1132
|
+
attr_reader :id, :name, :title, :type,
|
1133
|
+
:area, :classification, :max_level, :min_level
|
1134
|
+
|
1135
|
+
def initialize(elem)
|
1136
|
+
@id = elem[:id].to_i
|
1137
|
+
@name = elem[:name]
|
1138
|
+
@title = elem[:title]
|
1139
|
+
@type = elem[:type]
|
1140
|
+
@area = elem[:area]
|
1141
|
+
@classification = elem[:classification].to_i
|
1142
|
+
@max_level = elem[:maxLevel].to_i
|
1143
|
+
@min_level = elem[:minLevel].to_i
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
|
1148
|
+
|
1149
|
+
|
1150
|
+
# TODO rename
|
1151
|
+
# There is some sort of opposite relationship between PlansFor and CreatedBy
|
1152
|
+
class ItemCreation
|
1153
|
+
attr_reader :name, :id, :icon,
|
1154
|
+
:item, :reagents
|
1155
|
+
|
1156
|
+
def initialize(elem)
|
1157
|
+
@name = elem[:name]
|
1158
|
+
@id = elem[:id].to_i
|
1159
|
+
@icon = elem[:icon]
|
1160
|
+
|
1161
|
+
# not all items have reagents, some are just spells
|
1162
|
+
if (elem%'reagent')
|
1163
|
+
@reagents = []
|
1164
|
+
(elem/:reagent).each do |reagent|
|
1165
|
+
@reagents << Reagent.new(reagent)
|
1166
|
+
end
|
1167
|
+
end
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
|
1172
|
+
# <plansFor>
|
1173
|
+
# <spell name="Shadowprowler's Chestguard" icon="trade_leatherworking" id="42731">
|
1174
|
+
# <item name="Shadowprowler's Chestguard" icon="inv_chest_plate11" type="Leather" level="105" id="33204" quality="4"></item>
|
1175
|
+
# <reagent name="Heavy Knothide Leather" icon="inv_misc_leatherscrap_11" id="23793" count="10"></reagent>
|
1176
|
+
# <reagent name="Bolt of Soulcloth" icon="inv_fabric_soulcloth_bolt" id="21844" count="16"></reagent>
|
1177
|
+
# <reagent name="Primal Earth" icon="inv_elemental_primal_earth" id="22452" count="12"></reagent>
|
1178
|
+
# <reagent name="Primal Shadow" icon="inv_elemental_primal_shadow" id="22456" count="12"></reagent>
|
1179
|
+
# <reagent name="Primal Nether" icon="inv_elemental_primal_nether" id="23572" count="2"></reagent>
|
1180
|
+
# </spell>
|
1181
|
+
# </plansFor>
|
1182
|
+
class ItemPlansFor < ItemCreation
|
1183
|
+
def initialize(elem)
|
1184
|
+
super(elem)
|
1185
|
+
# TODO: Multiple items?
|
1186
|
+
@item = CreatedItem.new(elem%'item') if (elem%'item')
|
1187
|
+
end
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
# <createdBy>
|
1191
|
+
# <spell name="Bracing Earthstorm Diamond" icon="temp" id="32867">
|
1192
|
+
# <item requiredSkill="Jewelcrafting" name="Design: Bracing Earthstorm Diamond" icon="inv_scroll_03" type="Jewelcrafting" level="73" id="25903" requiredSkillRank="365" quality="1"></item>
|
1193
|
+
# <reagent name="Earthstorm Diamond" icon="inv_misc_gem_diamond_04" id="25867" count="1"></reagent>
|
1194
|
+
# </spell>
|
1195
|
+
# </createdBy>
|
1196
|
+
class ItemCreatedBy < ItemCreation
|
1197
|
+
def initialize(elem)
|
1198
|
+
super(elem)
|
1199
|
+
# TODO: Multiple items?
|
1200
|
+
@item = PlanItem.new(elem%'item') if (elem%'item')
|
1201
|
+
end
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
# TODO: Might be better to reuse an existing Item class?
|
1205
|
+
# <item name="Shadowprowler's Chestguard" icon="inv_chest_plate11" type="Leather" level="105" id="33204" quality="4"></item>
|
1206
|
+
class CreatedItem < Item
|
1207
|
+
attr_reader :type, :level, :quality
|
1208
|
+
|
1209
|
+
def initialize(elem)
|
1210
|
+
super(elem)
|
1211
|
+
@type = elem[:type]
|
1212
|
+
@level = elem[:level].to_i
|
1213
|
+
@quality = elem[:quality].to_i
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
|
1217
|
+
# TODO: Might be better to reuse an existing Item class?
|
1218
|
+
# <item requiredSkill="Jewelcrafting" name="Design: Bracing Earthstorm Diamond" icon="inv_scroll_03" type="Jewelcrafting" level="73" id="25903" requiredSkillRank="365" quality="1"></item>
|
1219
|
+
class PlanItem < Item
|
1220
|
+
attr_reader :required_skill, :type, :required_skill_rank, :level, :quality
|
1221
|
+
|
1222
|
+
def initialize(elem)
|
1223
|
+
super(elem)
|
1224
|
+
@type = elem[:type]
|
1225
|
+
@level = elem[:level].to_i
|
1226
|
+
@quality = elem[:quality].to_i
|
1227
|
+
@required_skill = elem[:requiredSkill]
|
1228
|
+
@required_skill_rank = elem[:requiredSkillRank].to_i
|
1229
|
+
end
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
class Reagent
|
1233
|
+
attr_reader :id, :name, :icon, :count
|
1234
|
+
|
1235
|
+
def initialize(elem)
|
1236
|
+
@id = elem[:id].to_i
|
1237
|
+
@name = elem[:name]
|
1238
|
+
@icon = elem[:icon]
|
1239
|
+
@count = elem[:count].to_i
|
1240
|
+
end
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
|
1244
|
+
|
1245
|
+
# A group of individuals
|
1246
|
+
# Note that search results don't contain the members
|
1247
|
+
class ArenaTeam
|
1248
|
+
attr_reader :name, :size, :battle_group, :faction, :faction_id, :realm, :realm_url,
|
1249
|
+
:games_played, :games_won, :ranking, :rating,
|
1250
|
+
:season_games_played, :season_games_won, :last_season_ranking,
|
1251
|
+
:relevance, :url, :url_escape,
|
1252
|
+
:characters, # can be blank on search results
|
1253
|
+
:emblem
|
1254
|
+
|
1255
|
+
def initialize(elem)
|
1256
|
+
@name = elem[:name]
|
1257
|
+
@size = elem[:size].to_i
|
1258
|
+
@battle_group = elem[:battleGroup]
|
1259
|
+
@faction = elem[:faction]
|
1260
|
+
@faction_id = elem[:factionId].to_i
|
1261
|
+
@realm = elem[:realm]
|
1262
|
+
@realm_url = elem[:realmUrl]
|
1263
|
+
|
1264
|
+
@games_played = elem[:gamesPlayed].to_i
|
1265
|
+
@games_won = elem[:gamesWon].to_i
|
1266
|
+
@ranking = elem[:ranking].to_i
|
1267
|
+
@rating = elem[:rating].to_i
|
1268
|
+
|
1269
|
+
@season_games_played = elem[:seasonGamesPlayed].to_i
|
1270
|
+
@season_games_won = elem[:seasonGamesWon].to_i
|
1271
|
+
@last_season_ranking = elem[:lastSeasonRanking].to_i
|
1272
|
+
|
1273
|
+
@relevance = elem[:relevance].to_i
|
1274
|
+
@url = elem[:url]
|
1275
|
+
|
1276
|
+
@emblem = ArenaTeamEmblem.new(elem%'emblem')
|
1277
|
+
|
1278
|
+
@members = []
|
1279
|
+
(elem%'members'/:character).each do |character|
|
1280
|
+
@members << Character.new(character)
|
1281
|
+
end
|
1282
|
+
end
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
class ArenaTeamEmblem
|
1286
|
+
attr_reader :background, :border_color, :border_style, :icon_colour, :icon_style
|
1287
|
+
|
1288
|
+
def initialize(elem)
|
1289
|
+
@background = elem[:background]
|
1290
|
+
@border_color = elem[:borderColor]
|
1291
|
+
@border_style = elem[:borderStyle].to_i
|
1292
|
+
@icon_color = elem[:iconColor]
|
1293
|
+
@icon_style = elem[:iconStyle].to_i
|
1294
|
+
end
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
|
1298
|
+
end
|
1299
|
+
end
|