wowr 0.2.2 → 0.3.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.
@@ -1,10 +1,11 @@
1
1
  module Wowr
2
2
  module Exceptions
3
- def self.raise_me(code)
4
- #msg = "(#{code.to_s}) #{msg}"
3
+ def self.raise_me(code, options = {})
5
4
  case code
6
5
  when "noCharacter"
7
- raise CharacterNotFound.new("Character with that name not found.")
6
+ raise CharacterNotFound.new("Character '#{options[:character_name]}' not found.")
7
+ else
8
+ raise StandardError.new("The XML returned an error: #{code.to_s}")
8
9
  end
9
10
  end
10
11
 
@@ -15,28 +16,92 @@ module Wowr
15
16
  end
16
17
 
17
18
  class ServerDoesNotExist < StandardError
19
+ def initialize(string)
20
+ super "Server at '#{string}' did not respond."
21
+ end
22
+ end
23
+
24
+ class CharacterNameNotSet < StandardError
25
+ def initialize
26
+ super "Character name not set in options or API constructor."
27
+ end
18
28
  end
19
29
 
20
- # errCode="noCharacter"
21
- class CharacterNotFound < StandardError
30
+ class GuildNameNotSet < StandardError
31
+ def initialize
32
+ super "Guild name not set in options or API constructor."
33
+ end
22
34
  end
23
35
 
24
- class ItemNotFound < StandardError
36
+ class ArenaTeamNameNotSet < StandardError
37
+ def initialize
38
+ super "Arena team name not set."
39
+ end
25
40
  end
26
41
 
27
- class GuildNotFound < StandardError
42
+ class InvalidArenaTeamSize < StandardError
28
43
  end
29
44
 
45
+ class RealmNotSet < StandardError
46
+ def initialize
47
+ super "Realm not set in options or API constructor."
48
+ end
49
+ end
50
+
51
+ # Search (fold)
30
52
  class SearchError < StandardError
31
53
  end
32
54
 
33
- class InvalidSearchType < StandardError
55
+ class InvalidSearchType < SearchError
56
+ def initialize(string)
57
+ super "'#{string}' is not a valid search type."
58
+ end
34
59
  end
35
60
 
36
- class NoSearchString < StandardError
61
+ class NoSearchString < SearchError
62
+ def initialize
63
+ super "No search string specified or string was empty."
64
+ end
37
65
  end
38
66
 
39
- class InvalidArenaTeamSize < StandardError
67
+ class ElementNotFoundError < StandardError
68
+ end
69
+
70
+ class CharacterNotFound < ElementNotFoundError
71
+ def initialize(string)
72
+ super "Character not found with name '#{string}'."
73
+ end
74
+ end
75
+
76
+ class ItemNotFound < ElementNotFoundError
77
+ def initialize(string)
78
+ super "Item not found with name '#{string}'."
79
+ end
80
+ end
81
+
82
+ class GuildNotFound < ElementNotFoundError
83
+ def initialize(string)
84
+ super "Guild not found with name '#{string}'."
85
+ end
86
+ end
87
+
88
+ class ArenaTeamNotFound < ElementNotFoundError
89
+ def initialize(string)
90
+ super "Arena team not found with name '#{string}'."
91
+ end
92
+ end
93
+ # (end)
94
+
95
+ class InvalidIconSize < StandardError
96
+ def initialize(array)
97
+ super "Icon size must be: #{array.keys.inspect}"
98
+ end
99
+ end
100
+
101
+ class InvalidIconType < StandardError
102
+ def initialize(array)
103
+ super "Icon type must be: #{array.keys.inspect}"
104
+ end
40
105
  end
41
106
  end
42
107
  end
@@ -1,5 +1,67 @@
1
1
  module Wowr
2
2
  module Extensions
3
3
 
4
+ # Rails's cattr_ things. activesupport/lib/active_support/core_ext/class
5
+ # Really quite handy.
6
+ # Thanks Reve's Lisa Seelye
7
+ module Class #:nodoc:
8
+ def cattr_reader(*syms) #:nodoc:
9
+ syms.flatten.each do |sym|
10
+ next if sym.is_a?(Hash)
11
+ class_eval(<<-EOS, __FILE__, __LINE__)
12
+ unless defined? @@#{sym}
13
+ @@#{sym} = nil
14
+ end
15
+
16
+ def self.#{sym}
17
+ @@#{sym}
18
+ end
19
+
20
+ def #{sym}
21
+ @@#{sym}
22
+ end
23
+ EOS
24
+ end
25
+ end
26
+
27
+ def cattr_writer(*syms) #:nodoc:
28
+ options = syms.last.is_a?(Hash) ? syms.pop : {}
29
+ syms.flatten.each do |sym|
30
+ class_eval(<<-EOS, __FILE__, __LINE__)
31
+ unless defined? @@#{sym}
32
+ @@#{sym} = nil
33
+ end
34
+
35
+ def self.#{sym}=(obj)
36
+ @@#{sym} = obj
37
+ end
38
+ #{"
39
+ def #{sym}=(obj)
40
+ @@#{sym} = obj
41
+ end
42
+ " unless options[:instance_writer] == false }
43
+ EOS
44
+ end
45
+ end
46
+ def cattr_accessor(*syms) #:nodoc:
47
+ cattr_reader(*syms)
48
+ cattr_writer(*syms)
49
+ end
50
+
51
+
52
+ def stringy(sym) #:nodoc:
53
+ class_eval(<<-EOS, __FILE__, __LINE__)
54
+ def to_s
55
+ @#{sym}
56
+ end
57
+ EOS
58
+ end
59
+ end
60
+
4
61
  end
5
- end
62
+ end
63
+
64
+
65
+ class Class #:nodoc:
66
+ include Wowr::Extensions::Class
67
+ end
@@ -0,0 +1,85 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'character.rb'
5
+
6
+ module Wowr #:nodoc:
7
+ module Classes #:nodoc:
8
+
9
+ # A player guild containing members
10
+ # Abstract
11
+ class Guild
12
+ attr_reader :name, :url, :realm
13
+ # :roster_url, :stats_url, :stats_url_escape,
14
+ alias_method :to_s, :name
15
+
16
+ def initialize(elem)
17
+ if (elem%'guildKey')
18
+ guild = (elem%'guildKey')
19
+ else
20
+ guild = elem
21
+ end
22
+
23
+ @name = guild[:name]
24
+ @url = guild[:url]
25
+ @realm = guild[:realm]
26
+ end
27
+ end
28
+
29
+ # Basic search information returned by the search.xml
30
+ # <guilds>
31
+ # <guild
32
+ # battleGroup="Ruin"
33
+ # faction="Alliance"
34
+ # factionId="0"
35
+ # name="HAND"
36
+ # realm="Stormrage"
37
+ # relevance="100"
38
+ # url="r=Stormrage&amp;n=HAND&amp;p=1"/>
39
+ # </guilds>
40
+ class SearchGuild < Guild
41
+ attr_reader :faction, :faction_id, :battle_group
42
+
43
+ def initialize(elem)
44
+ super(elem)
45
+
46
+ @battle_group = elem[:battleGroup]
47
+ @faction = elem[:faction]
48
+ @faction_id = elem[:factionId].to_i
49
+
50
+ @relevance = elem[:relevance].to_i
51
+ end
52
+ end
53
+
54
+ # Full guild data
55
+ # <guildKey factionId="0" name="HAND" nameUrl="HAND" realm="Stormrage" realmUrl="Stormrage" url="r=Stormrage&amp;n=HAND"/>
56
+ # <guildInfo>
57
+ # <guild>
58
+ # <members filterField="" filterValue="" maxPage="1" memberCount="1" page="1" sortDir="a" sortField="">
59
+ # <character class="Paladin" classId="2" gender="Male" genderId="0" level="14" name="Sturky" race="Dwarf" raceId="3" rank="0" url="r=Stormrage&amp;n=Sturky"/>
60
+ # </members>
61
+ # </guild>
62
+ # </guildInfo>
63
+ class FullGuild < Guild
64
+ attr_reader :members, :name_url, :realm_url #, :member_count
65
+
66
+ def initialize(elem)
67
+ super(elem)
68
+
69
+ @name_url = elem[:nameUrl]
70
+ @realm_url = elem[:realmUrl]
71
+
72
+ # Guild/guild_id/guild_url not set for characters
73
+ if (elem%'guildInfo')
74
+ # @member_count = (elem%'guildInfo'%'guild'%'members')[:memberCount].to_i || nil
75
+ @members = {}
76
+ (elem%'guildInfo'%'guild'%'members'/:character).each do |char|
77
+ # TODO: Change to search character?
78
+ members[char[:name]] = Character.new(char)
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,616 @@
1
+ # TODO: Item sources - Vendors
2
+ # sourceType.vendor
3
+ # sourceType.questReward
4
+ # sourceType.createdBySpell
5
+
6
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+
9
+ module Wowr
10
+ module Classes
11
+
12
+ # Most basic
13
+ # Composed of an ItemInfo and
14
+ # Needs to be consolidated with ItemInfo and other stuff
15
+ # to be a parent class that they extend?
16
+ # TODO: At the moment needs a reference to the API in order to get the base URL for icons
17
+ # TODO: Make extend Icon class
18
+ class Item
19
+ attr_reader :id, :name, :icon_base
20
+ alias_method :item_id, :id
21
+ alias_method :to_s, :name
22
+ alias_method :to_i, :id
23
+
24
+ @@icon_url_base = 'images/icons/'
25
+ @@icon_sizes = {:large => ['64x64', 'jpg'], :medium => ['43x43', 'png'], :small => ['21x21', 'png']}
26
+
27
+ def initialize(elem, api = nil)
28
+ @api = api
29
+
30
+ @id = elem[:id].to_i
31
+ @name = elem[:name]
32
+ @icon_base = elem[:icon]
33
+ end
34
+
35
+ def icon(size = :medium)
36
+ if !@@icon_sizes.include?(size)
37
+ raise Wowr::Exceptions::InvalidIconSize.new(@@icon_sizes)
38
+ end
39
+
40
+ if @api
41
+ base = @api.base_url
42
+ else
43
+ base = 'http://www.wowarmory.com/'
44
+ end
45
+
46
+ # http://www.wowarmory.com/images/icons/64x64/blahblah.jpg
47
+ return base + @@icon_url_base + @@icon_sizes[size][0] + '/' + @icon_base + '.' + @@icon_sizes[size][1]
48
+ end
49
+ end
50
+
51
+ # Full data from item-info and item-tooltip
52
+ class FullItem < Item
53
+
54
+ def initialize(info, tooltip, api = nil)
55
+ super(info, api)
56
+ @info = ItemInfo.new(info, api)
57
+ @tooltip = ItemTooltip.new(tooltip, api)
58
+ end
59
+
60
+ def method_missing(m, *args)
61
+ begin
62
+ return @info.send(m, *args)
63
+ rescue NoMethodError => e
64
+ begin
65
+ return @tooltip.send(m, *args)
66
+ rescue
67
+ raise NoMethodError.new("undefined method '#{m}' for #{self.class}")
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+
75
+
76
+ # uses item-info.xml
77
+ class ItemInfo < Item
78
+ attr_reader :level, :quality, :type,
79
+ :cost, :disenchant_items, :disenchant_skill_rank, :vendors,
80
+ :objective_of_quests,
81
+ :reward_from_quests,
82
+ :drop_creatures,
83
+ :plans_for, :created_by
84
+
85
+ alias_method :disenchants, :disenchant_items
86
+
87
+ def initialize(elem, api = nil)
88
+ super(elem, api)
89
+
90
+ @level = elem[:level].to_i
91
+ @quality = elem[:quality].to_i
92
+ @type = elem[:type]
93
+
94
+ # Cost can be in gold, or tokens
95
+ @cost = ItemCost.new(elem%'cost') if (elem%'cost')
96
+
97
+
98
+
99
+ # is costs really an array?
100
+ #@costs = []
101
+ #(elem/:cost).each do |cost|
102
+ # @costs << ItemCost.new(cost)
103
+ #end
104
+
105
+ etc = [
106
+ ['disenchantLoot', '@disenchant_items', 'item', DisenchantItem, true],
107
+ ['objectiveOfQuests', '@objective_of_quests', 'quest', ItemQuest, false],
108
+ ['rewardFromQuests', '@reward_from_quests', 'quest', ItemQuest, false],
109
+ ['vendors', '@vendors', 'creature', ItemVendor, false],
110
+ ['dropCreatures', '@drop_creatures', 'creature', ItemDropCreature, false],
111
+ ['plansFor', '@plans_for', 'spell', ItemPlansFor, true],
112
+ ['createdBy', '@created_by', 'spell', ItemCreatedBy, true],
113
+ ]
114
+
115
+ etc.each do |b|
116
+ ele = b[0]
117
+ var = b[1]
118
+ list = b[2]
119
+ my_class = b[3]
120
+ requires_api = b[4]
121
+
122
+ if elem%ele
123
+ tmp_arr = []
124
+ (elem%ele/list).each do |x|
125
+ if requires_api
126
+ tmp_arr << my_class.new(x, api)
127
+ else
128
+ tmp_arr << my_class.new(x)
129
+ end
130
+ end
131
+ self.instance_variable_set(var, tmp_arr)
132
+ end
133
+ end
134
+
135
+ if (elem%'disenchantLoot')
136
+ @disenchant_skill_rank = (elem%'disenchantLoot')[:requiredSkillRank].to_i
137
+
138
+ # @disenchant_items = []
139
+ # (elem%'disenchantLoot'/:item).each do |item|
140
+ # @disenchant_items << DisenchantItem.new(item)
141
+ # end
142
+ end
143
+
144
+ # if (elem%'objectiveOfQuests')
145
+ # @objective_of_quests = []
146
+ # (elem%'objectiveOfQuests'/:quest).each do |quest|
147
+ # @objective_of_quests << ItemQuest.new(quest)
148
+ # end
149
+ # end
150
+ #
151
+ # if (elem%'rewardFromQuests')
152
+ # @reward_from_quests = []
153
+ # (elem%'rewardFromQuests'/:quest).each do |quest|
154
+ # @reward_from_quests << ItemQuest.new(quest)
155
+ # end
156
+ # end
157
+ #
158
+ # if (elem%'vendors')
159
+ # @vendors = []
160
+ # (elem%'vendors'/:creature).each do |vendor|
161
+ # @vendors << ItemVendor.new(vendor)
162
+ # end
163
+ # end
164
+ #
165
+ # if (elem%'dropCreatures')
166
+ # @drop_creatures = []
167
+ # (elem%'dropCreatures'/:creature).each do |creature|
168
+ # @drop_creatures << ItemDropCreature.new(creature)
169
+ # end
170
+ # end
171
+ #
172
+ # if (elem%'plansFor')
173
+ # @plans_for = []
174
+ # (elem%'plansFor'/:spell).each do |plan|
175
+ # @plans_for << ItemPlansFor.new(plan)
176
+ # end
177
+ # end
178
+ #
179
+ # if (elem%'createdBy')
180
+ # @created_by = []
181
+ # (elem%'createdBy'/:spell).each do |c|
182
+ # @created_by << ItemCreatedBy.new(c)
183
+ # end
184
+ # end
185
+ end
186
+ end
187
+
188
+
189
+
190
+ # Provides detailed item information
191
+ # Note that the item-tooltip.xml just returns an empty document when the item
192
+ # can't be found.
193
+ class ItemTooltip < Item
194
+ attr_reader :desc, :overall_quality_id, :bonding, :max_count, #:id, :name, :icon,
195
+ :class_id, :bonuses, :item_source,
196
+ :bonuses, :resistances,
197
+ :required_level,
198
+ :allowable_classes,
199
+ :armor, :durability,
200
+ :sockets, :socket_match_enchant,
201
+ :gem_properties
202
+ alias_method :description, :desc
203
+
204
+ def initialize(elem, api = nil)
205
+ super(elem, api)
206
+ @id = (elem%'id').html.to_i
207
+ @name = (elem%'name').html
208
+ @icon_base = (elem%'icon').html
209
+ @desc = (elem%'desc').html if (elem%'desc')
210
+ @overall_quality_id = (elem%'overallQualityId').html.to_i
211
+ @bonding = (elem%'bonding').html.to_i
212
+ @stackable = (elem%'stackable').html.to_i if (elem%'stackable')
213
+ @max_count = (elem%'maxCount').html.to_i if (elem%'maxCount')
214
+ @class_id = (elem%'classId').html.to_i
215
+ @required_level = (elem%'requiredLevel').html.to_i if (elem%'requiredLevel')
216
+
217
+ @equipData = ItemEquipData.new(elem%'equipData')
218
+
219
+ # TODO: This appears to be a plain string at the moment
220
+ #<gemProperties>+26 Healing +9 Spell Damage and 2% Reduced Threat</gemProperties>
221
+ @gem_properties = (elem%'gemProperties').html if (elem%'gemProperties')
222
+
223
+ # not all items have damage data
224
+ @damage = ItemDamageData.new(elem%'damageData') if !(elem%'damageData').html.empty?
225
+
226
+
227
+ # TODO: Test socket data with a variety of items
228
+ # TODO: replace with socket Class?
229
+ if (elem%'socketData')
230
+ @sockets = []
231
+ (elem%'socketData'/:socket).each do |socket|
232
+ @sockets << socket[:color]
233
+ end
234
+
235
+ @socket_match_enchant = (elem%'socketData'%'socketMatchEnchant')
236
+ end
237
+
238
+
239
+ # When there is no data, stats are not present in @bonuses
240
+ # TODO: When there is no stats at all, @bonuses shouldn't be set
241
+ @bonuses = {}
242
+
243
+ bonus_stats = {
244
+ :strength => :bonusStrength,
245
+ :agility => :bonusAgility,
246
+ :stamina => :bonusStamina,
247
+ :intellect => :bonusIntellect,
248
+ :spirit => :bonusSpirit
249
+ }
250
+ bonus_stats.each do |stat, xml_elem|
251
+ @bonuses[stat] = test_stat(elem/xml_elem) if test_stat(elem/xml_elem)
252
+ end
253
+
254
+ # Resistances
255
+ @resistances = {}
256
+
257
+ resist_stats = {
258
+ :arcane => :arcaneResist,
259
+ :fire => :fireResist,
260
+ :frost => :frostResist,
261
+ :holy => :holyResist,
262
+ :nature => :natureResist,
263
+ :shadow => :shadowResist
264
+ }
265
+ resist_stats.each do |stat, xml_elem|
266
+ @resistances[stat] = test_stat(elem/xml_elem) if test_stat(elem/xml_elem)
267
+ end
268
+
269
+
270
+ if (elem%'allowableClasses')
271
+ @allowable_classes = []
272
+ (elem%'allowableClasses'/:class).each do |klass|
273
+ @allowable_classes << klass.html
274
+ end
275
+ end
276
+
277
+ # NOTE not representing armor bonus
278
+ @armor = (elem%'armor').html.to_i if (elem%'armor')
279
+
280
+ # NOTE not representing max
281
+ @durability = (elem%'durability')[:current].to_i if (elem%'durability')
282
+
283
+ if (elem%'spellData')
284
+ @spells = []
285
+ (elem%'spellData'/:spell).each do |spell|
286
+ @spells << ItemSpell.new(spell)
287
+ end
288
+ end
289
+
290
+ @setData = ItemSetData.new(elem%'setData') if (elem%'setData')
291
+
292
+ # @item_sources = []
293
+ # (elem/:itemSource).each do |source|
294
+ # @item_sources << ItemSource.new(source)
295
+ # end
296
+ @item_source = ItemSource.new(elem%'itemSource') if (elem%'itemSource') # TODO: More than once source?
297
+ end
298
+
299
+ private
300
+ def test_stat(elem)
301
+ if elem
302
+ if !elem.html.empty?
303
+ return elem.html.to_i
304
+ end
305
+ end
306
+ return nil
307
+ end
308
+ end
309
+
310
+ class ItemEquipData
311
+ attr_reader :inventory_type, :subclass_name, :container_slots
312
+
313
+ def initialize(elem)
314
+ @inventory_type = (elem%'inventoryType').html.to_i
315
+ @subclass_name = (elem%'subclassName').html if (elem%'subclassName')
316
+ @container_slots = (elem%'containerSlots').html.to_i if (elem%'containerSlots') # for baggies
317
+ end
318
+ end
319
+
320
+ class ItemSetData
321
+ attr_reader :name, :items, :set_bonuses
322
+ alias_method :to_s, :name
323
+
324
+ def initialize(elem)
325
+ @name = elem[:name]
326
+
327
+ @items = []
328
+ (elem/:item).each do |item|
329
+ @items << item[:name]
330
+ end
331
+
332
+ @set_bonuses = []
333
+ (elem/:setBonus).each do |bonus|
334
+ @set_bonuses << ItemSetBonus.new(bonus)
335
+ end
336
+ end
337
+ end
338
+
339
+ class ItemSetBonus
340
+ attr_reader :threshold, :description
341
+ alias_method :desc, :description
342
+ alias_method :to_s, :description
343
+
344
+ def initialize(elem)
345
+ @threshold = elem[:threshold].to_i
346
+ @description = elem[:desc]
347
+ end
348
+ end
349
+
350
+ class ItemSpell
351
+ attr_reader :trigger, :description
352
+ alias_method :desc, :description
353
+ alias_method :to_s, :description
354
+
355
+ def initialize(elem)
356
+ @trigger = (elem%'trigger').html.to_i
357
+ @description = (elem%'desc').html
358
+ end
359
+ end
360
+
361
+ class ItemDamageData
362
+ attr_reader :type, :min, :max, :speed, :dps
363
+
364
+ def initialize(elem)
365
+ @type = (elem%'damage'%'type').html.to_i
366
+ @min = (elem%'damage'%'min').html.to_i
367
+ @max = (elem%'damage'%'max').html.to_i
368
+ @speed = (elem%'speed').html.to_i
369
+ @dps = (elem%'dps').html.to_f
370
+ end
371
+ end
372
+
373
+ class ItemSource
374
+ attr_reader :value,
375
+ :area_id, :area_name,
376
+ :creature_id, :creature_name,
377
+ :difficulty, :drop_rate
378
+
379
+ def initialize(elem)
380
+ @value = elem[:value]
381
+ @area_id = elem[:areaId].to_i if elem[:areaId]
382
+ @area_name = elem[:areaName] if elem[:areaName]
383
+ @creature_id = elem[:creatureId].to_i if elem[:creatureId]
384
+ @creature_name = elem[:creatureName] if elem[:creatureName]
385
+ @difficulty = elem[:difficulty] if elem[:difficulty]
386
+ @drop_rate = elem[:dropRate].to_i if elem[:dropRate]
387
+ @required_level = elem[:reqLvl].to_i if elem[:reqLvl]
388
+ end
389
+ end
390
+
391
+
392
+
393
+ # A really basic item type returned by searches
394
+ class SearchItem < Item
395
+ attr_reader :url, :rarity,
396
+ :source, :item_level, :relevance
397
+ alias_method :level, :item_level
398
+
399
+ def initialize(elem, api = nil)
400
+ super(elem, api)
401
+ @rarity = elem[:rarity].to_i
402
+ @url = elem[:url]
403
+
404
+ @item_level = elem.at("filter[@name='itemLevel']")[:value].to_i
405
+ @source = elem.at("filter[@name='source']")[:value]
406
+ @relevance = elem.at("filter[@name='relevance']")[:value].to_i
407
+ end
408
+ end
409
+
410
+
411
+
412
+
413
+ # <rewardFromQuests>
414
+ # <quest name="Justice Dispensed" level="39" reqMinLevel="30" id="11206" area="Dustwallow Marsh" suggestedPartySize="0"></quest>
415
+ # <quest name="Peace at Last" level="39" reqMinLevel="30" id="11152" area="Dustwallow Marsh" suggestedPartySize="0"></quest>
416
+ # </rewardFromQuests>
417
+ # TODO: Rename
418
+ class ItemQuest
419
+ attr_reader :name, :id, :level, :min_level, :area, :suggested_party_size
420
+
421
+ def initialize(elem)
422
+ @name = elem[:name]
423
+ @id = elem[:id].to_i
424
+ @level = elem[:level].to_i
425
+ @min_level = elem[:min_level].to_i
426
+ @area = elem[:area]
427
+ @suggested_party_size = elem[:suggested_party_size].to_i
428
+ end
429
+ end
430
+
431
+
432
+
433
+ # Creatures that drop the item
434
+ # <creature name="Giant Marsh Frog" minLevel="1" type="Critter" maxLevel="1" dropRate="6" id="23979" classification="0" area="Dustwallow Marsh"></creature>
435
+ # <creature name="Nalorakk" minLevel="73" title="Bear Avatar" url="fl[source]=dungeon&amp;fl[difficulty]=normal&amp;fl[boss]=23576" type="Humanoid" maxLevel="73" dropRate="2" id="23576" classification="3" areaUrl="fl[source]=dungeon&amp;fl[boss]=all&amp;fl[difficulty]=normal&amp;fl[dungeon]=3805" area="Zul'Aman"></creature>
436
+ class ItemDropCreature
437
+ attr_reader :name, :id, :type, :min_level, :max_level, :drop_rate, :classification, :area
438
+
439
+ def initialize(elem)
440
+ @name = elem[:name]
441
+ @id = elem[:id].to_i
442
+ @min_level = elem[:minLevel].to_i
443
+ @max_level = elem[:maxLevel].to_i
444
+ @drop_rate = elem[:dropRate].to_i
445
+ @classification = elem[:classification].to_i
446
+ @area = elem[:area]
447
+
448
+ # optional boss stuff
449
+ @title = elem[:title] if elem[:title] # TODO: not nil when no property?
450
+ @url = elem[:url] if elem[:url]
451
+ @type = elem[:type] if elem[:type] # Humanoid etc.
452
+ @area_url = elem[:areaUrl] if elem[:areaUrl]
453
+ end
454
+ end
455
+
456
+ # Cost can be gold or a set of required tokens
457
+ # See ItemCostToken
458
+ # <cost sellPrice="280" buyPrice="5600"></cost>
459
+ # <cost>
460
+ # <token icon="spell_holy_championsbond" id="29434" count="60"></token>
461
+ # </cost>
462
+ class ItemCost
463
+ attr_reader :buy_price, :sell_price, :tokens
464
+
465
+ def initialize(elem)
466
+ @buy_price = elem[:buyPrice].to_i if elem[:buyPrice]
467
+ @sell_price = elem[:sellPrice].to_i if elem[:sellPrice]
468
+
469
+ if (elem%'token')
470
+ @tokens = []
471
+ (elem/:token).each do |token|
472
+ @tokens << ItemCostToken.new(token)
473
+ end
474
+ end
475
+ end
476
+ end
477
+
478
+ # <token icon="spell_holy_championsbond" id="29434" count="60"></token>
479
+ class ItemCostToken < Item
480
+ attr_reader :count
481
+
482
+ def initialize(elem, api = nil)
483
+ super(elem)
484
+ # @id = elem[:id].to_i
485
+ # @icon_bse = elem[:icon]
486
+ @count = elem[:count].to_i
487
+ end
488
+ end
489
+
490
+ # <item name="Void Crystal" minCount="1" maxCount="2" icon="inv_enchant_voidcrystal" type="Enchanting" level="70" dropRate="6" id="22450" quality="4"></item>
491
+ class DisenchantItem < Item
492
+ attr_reader :level, :type, :drop_rate, :min_count, :max_count, :quality
493
+ # :name, :id, :icon,
494
+
495
+ def initialize(elem, api = nil)
496
+ super(elem, api)
497
+ # @name = elem[:name]
498
+ # @id = elem[:id].to_i
499
+ # @icon = elem[:icon]
500
+ @level = elem[:level].to_i
501
+ @type = elem[:type]
502
+ @drop_rate = elem[:dropRate].to_i
503
+ @min_count = elem[:minCount].to_i
504
+ @max_count = elem[:maxCount].to_i
505
+ @quality = elem[:quality].to_i
506
+ end
507
+ end
508
+
509
+
510
+ class ItemVendor
511
+ attr_reader :id, :name, :title, :type,
512
+ :area, :classification, :max_level, :min_level
513
+ alias_method :to_s, :name
514
+ alias_method :to_i, :id
515
+
516
+ def initialize(elem)
517
+ @id = elem[:id].to_i
518
+ @name = elem[:name]
519
+ @title = elem[:title]
520
+ @type = elem[:type]
521
+ @area = elem[:area]
522
+ @classification = elem[:classification].to_i
523
+ @max_level = elem[:maxLevel].to_i
524
+ @min_level = elem[:minLevel].to_i
525
+ end
526
+ end
527
+
528
+
529
+
530
+ # TODO rename
531
+ # There is some sort of opposite relationship between PlansFor and CreatedBy
532
+ class ItemCreation < Item
533
+ attr_reader :item, :reagents
534
+
535
+ def initialize(elem, api = nil)
536
+ super(elem, api)
537
+
538
+ if (elem%'reagent')
539
+ @reagents = []
540
+ (elem/:reagent).each do |reagent|
541
+ @reagents << Reagent.new(reagent, api)
542
+ end
543
+ end
544
+ end
545
+ end
546
+
547
+ # (fold)
548
+ # <plansFor>
549
+ # <spell name="Shadowprowler's Chestguard" icon="trade_leatherworking" id="42731">
550
+ # <item name="Shadowprowler's Chestguard" icon="inv_chest_plate11" type="Leather" level="105" id="33204" quality="4"></item>
551
+ # <reagent name="Heavy Knothide Leather" icon="inv_misc_leatherscrap_11" id="23793" count="10"></reagent>
552
+ # </spell>
553
+ # </plansFor>
554
+ # (end)
555
+ class ItemPlansFor < ItemCreation
556
+ def initialize(elem, api = nil)
557
+ super(elem, api)
558
+ # TODO: Multiple items?
559
+ @item = CreatedItem.new(elem%'item') if (elem%'item')
560
+ end
561
+ end
562
+
563
+ # <createdBy>
564
+ # <spell name="Bracing Earthstorm Diamond" icon="temp" id="32867">
565
+ # <item requiredSkill="Jewelcrafting" name="Design: Bracing Earthstorm Diamond" icon="inv_scroll_03" type="Jewelcrafting" level="73" id="25903" requiredSkillRank="365" quality="1"></item>
566
+ # <reagent name="Earthstorm Diamond" icon="inv_misc_gem_diamond_04" id="25867" count="1"></reagent>
567
+ # </spell>
568
+ # </createdBy>
569
+ class ItemCreatedBy < ItemCreation
570
+ def initialize(elem, api = nil)
571
+ super(elem, api)
572
+ # TODO: Multiple items?
573
+ @item = PlanItem.new(elem%'item') if (elem%'item')
574
+ end
575
+ end
576
+
577
+
578
+ # <item name="Shadowprowler's Chestguard" icon="inv_chest_plate11" type="Leather" level="105" id="33204" quality="4"></item>
579
+ class CreatedItem < Item
580
+ attr_reader :type, :level, :quality
581
+
582
+ def initialize(elem, api = nil)
583
+ super(elem, api)
584
+ @type = elem[:type]
585
+ @level = elem[:level].to_i
586
+ @quality = elem[:quality].to_i
587
+ end
588
+ end
589
+
590
+ # <item requiredSkill="Jewelcrafting" name="Design: Bracing Earthstorm Diamond" icon="inv_scroll_03" type="Jewelcrafting" level="73" id="25903" requiredSkillRank="365" quality="1"></item>
591
+ class PlanItem < Item
592
+ attr_reader :required_skill, :type, :required_skill_rank, :level, :quality
593
+
594
+ def initialize(elem, api = nil)
595
+ super(elem, api)
596
+ @type = elem[:type]
597
+ @level = elem[:level].to_i
598
+ @quality = elem[:quality].to_i
599
+ @required_skill = elem[:requiredSkill]
600
+ @required_skill_rank = elem[:requiredSkillRank].to_i
601
+ end
602
+ end
603
+
604
+ class Reagent < Item
605
+ attr_reader :count
606
+
607
+ def initialize(elem, api = nil)
608
+ super(elem, api)
609
+ # @id = elem[:id].to_i
610
+ # @name = elem[:name]
611
+ # @icon = elem[:icon]
612
+ @count = elem[:count].to_i
613
+ end
614
+ end
615
+ end
616
+ end