renchap-wowr 0.5.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.
@@ -0,0 +1,80 @@
1
+ module Wowr
2
+ module Classes
3
+ class ArenaTeam
4
+ attr_reader :name, :size, :battle_group, :faction, :faction_id, :realm, :realm_url,
5
+ :games_played, :games_won, :ranking, :rating,
6
+ :season_games_played, :season_games_won, :last_season_ranking,
7
+ :relevance, :url, :url_escape,
8
+ :characters, # can be blank on search results
9
+ :emblem
10
+ alias_method :to_s, :name
11
+
12
+ def initialize(elem)
13
+ @name = elem[:name]
14
+ @size = elem[:size].to_i
15
+ @battle_group = elem[:battleGroup]
16
+ @faction = elem[:faction]
17
+ @faction_id = elem[:factionId].to_i
18
+ @realm = elem[:realm]
19
+ @realm_url = elem[:realmUrl]
20
+
21
+ @games_played = elem[:gamesPlayed].to_i
22
+ @games_won = elem[:gamesWon].to_i
23
+ # @ranking = elem[:ranking].to_i # Ranking in the seach results is always 0
24
+ @rating = elem[:rating].to_i
25
+
26
+ @season_games_played = elem[:seasonGamesPlayed].to_i
27
+ @season_games_won = elem[:seasonGamesWon].to_i
28
+ @last_season_ranking = elem[:lastSeasonRanking].to_i
29
+
30
+ @relevance = elem[:relevance].to_i
31
+ @url = elem[:url]
32
+
33
+ @emblem = ArenaTeamEmblem.new(elem%'emblem')
34
+ end
35
+ end
36
+
37
+
38
+ class SearchArenaTeam < ArenaTeam
39
+ def initialize(elem)
40
+ super(elem)
41
+ end
42
+ end
43
+
44
+ # <teamInfo>
45
+ # <arenaTeam battleGroup="Vindication" faction="Alliance" factionId="0" gamesPlayed="6" gamesWon="1" lastSeasonRanking="4118" name="Monkey" ranking="6522" rating="1535" realm="Burning Blade" realmUrl="b=Vindication&amp;r=Burning+Blade&amp;ts=2&amp;t=Monkey&amp;ff=realm&amp;fv=Burning+Blade&amp;select=Monkey" relevance="0" seasonGamesPlayed="66" seasonGamesWon="34" size="2" url="r=Burning+Blade&amp;ts=2&amp;t=Monkey&amp;select=Monkey" urlEscape="r=Burning+Blade&amp;ts=2&amp;t=Monkey&amp;select=Monkey">
46
+ # <emblem background="ff70de56" borderColor="ffcc73eb" borderStyle="6" iconColor="ffe3b320" iconStyle="44"/>
47
+ # <members>
48
+ # <character battleGroup="Vindication" charUrl="r=Burning+Blade&amp;n=Fandiar" class="Priest" classId="5" contribution="1380" gamesPlayed="0" gamesWon="0" gender="Male" genderId="0" guild="Legends" guildId="916" guildUrl="r=Burning+Blade&amp;n=Legends&amp;p=1" name="Fandiar" race="Night Elf" raceId="4" realm="Burning Blade" seasonGamesPlayed="18" seasonGamesWon="1" teamRank="0"/>
49
+ # <character battleGroup="Vindication" charUrl="r=Burning+Blade&amp;n=Stayfrosty" class="Mage" classId="8" contribution="1460" gamesPlayed="6" gamesWon="1" gender="Male" genderId="0" guild="Legends" guildId="916" guildUrl="r=Burning+Blade&amp;n=Legends&amp;p=1" name="Stayfrosty" race="Human" raceId="1" realm="Burning Blade" seasonGamesPlayed="6" seasonGamesWon="1" teamRank="1"/>
50
+ # <character battleGroup="Vindication" charUrl="r=Burning+Blade&amp;n=Step" class="Rogue" classId="4" contribution="1688" gamesPlayed="2" gamesWon="1" gender="Female" genderId="1" guild="Legends" guildId="916" guildUrl="r=Burning+Blade&amp;n=Legends&amp;p=1" name="Step" race="Human" raceId="1" realm="Burning Blade" seasonGamesPlayed="44" seasonGamesWon="33" teamRank="1"/>
51
+ # </members>
52
+ # </arenaTeam>
53
+ # </teamInfo>
54
+ class FullArenaTeam < ArenaTeam
55
+
56
+ def initialize(elem)
57
+ super(elem)
58
+ # @ranking = elem[:ranking].to_i
59
+
60
+ @members = {}
61
+ (elem%'members'/:character).each do |character|
62
+ @members[character[:name]] = SearchCharacter.new(character)
63
+ end
64
+ end
65
+ end
66
+
67
+ # An arena team's logo
68
+ class ArenaTeamEmblem
69
+ attr_reader :background, :border_color, :border_style, :icon_colour, :icon_style
70
+
71
+ def initialize(elem)
72
+ @background = elem[:background]
73
+ @border_color = elem[:borderColor]
74
+ @border_style = elem[:borderStyle].to_i
75
+ @icon_color = elem[:iconColor]
76
+ @icon_style = elem[:iconStyle].to_i
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,78 @@
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
+ module Wowr
5
+ module Classes
6
+ class CommonCalendar
7
+ attr_reader :summary, :calendar_type, :start, :icon
8
+
9
+ def initialize(json, api = nil)
10
+ @summary = json["summary"]
11
+ @calendar_type = json["calendarType"]
12
+ @start = Time.at((json["start"] / 1000).floor)
13
+ @icon = json["icon"]
14
+ end
15
+ end
16
+
17
+ class WorldCalendar < CommonCalendar
18
+ attr_reader :end, :description, :priority
19
+
20
+ def initialize(json, api = nil)
21
+ super(json, api)
22
+ @end = Time.at((json["end"] / 1000).floor)
23
+ @description = json["description"]
24
+ @priority = json["priority"]
25
+ end
26
+ end
27
+
28
+ class UserCommonCalendar < CommonCalendar
29
+ attr_reader :type, :owner, :moderator, :id
30
+
31
+ def initialize(json, api = nil)
32
+ super(json, api)
33
+ @type = json["type"]
34
+ @owner = json["owner"]
35
+ @moderator = json["moderator"] if json["moderator"]
36
+ @id = json["id"]
37
+ end
38
+ end
39
+
40
+ class UserCalendar < UserCommonCalendar
41
+ attr_reader :inviter, :status
42
+
43
+ def initialize(json, api = nil)
44
+ super(json, api)
45
+ @inviter = json["inviter"]
46
+ @status = json["status"]
47
+ end
48
+ end
49
+
50
+ class UserDetailCalendar < UserCommonCalendar
51
+ attr_reader :locked, :description, :invites
52
+
53
+ def initialize(json, api = nil)
54
+ super(json, api)
55
+ @locked = json["locked"]
56
+ @description = json["description"]
57
+
58
+ @invites = []
59
+
60
+ json["invites"].each do |invitee|
61
+ @invites << UserDetailInvitee.new(invitee, api)
62
+ end
63
+ end
64
+ end
65
+
66
+ class UserDetailInvitee
67
+ attr_reader :class_id, :status, :moderator, :invitee, :id
68
+
69
+ def initialize(json, api = nil)
70
+ @class_id = json["class_id"]
71
+ @status = json["status"]
72
+ @moderator = json["moderator"]
73
+ @invitee = json["invitee"]
74
+ @id = json["id"]
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,816 @@
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 'wowr/item.rb'
5
+
6
+ module Wowr
7
+ module Classes
8
+
9
+ # Short character info, used in guild lists etc.
10
+ # Note that the way that searches and character listings within guilds works,
11
+ # there can be a variable amount of information filled in within the class.
12
+ # Guild listings and search results contain a smaller amount of information than
13
+ # single queries
14
+ # Attributes
15
+ # * name (String) - Full character name
16
+ # * level (Fixnum) - Level
17
+ # See Also: Guild
18
+ class Character
19
+ attr_reader :name, :level, :url, :rank,
20
+ :klass, :klass_id,
21
+ :gender, :gender_id,
22
+ :race, :race_id,
23
+ :guild, :guild_id, :guild_url,
24
+ :realm,
25
+ :battle_group, :last_login,
26
+ :relevance, :search_rank,
27
+
28
+ :season_games_played, :season_games_won, :team_rank, :contribution # From ArenaTeam info
29
+
30
+ alias_method :to_s, :name
31
+ alias_method :to_i, :level
32
+
33
+ @@race_icon_url_base = 'images/icons/race/'
34
+ @@class_icon_url_base = 'images/icons/class/'
35
+ @@portrait_url_base = 'images/portraits/'
36
+ @@icon_types = {:default => 'wow-default', 70 => 'wow-70', :other => 'wow'}
37
+
38
+ def initialize(elem, api = nil)
39
+ @api = api
40
+
41
+ @name = elem[:name]
42
+ @level = elem[:level].to_i
43
+ @url = elem[:url] || elem[:charUrl]
44
+ @rank = elem[:rank].to_i
45
+
46
+ @klass = elem[:class]
47
+ @klass_id = elem[:classId].to_i
48
+
49
+ @gender = elem[:gender]
50
+ @gender_id = elem[:genderId].to_i
51
+
52
+ @race = elem[:race]
53
+ @race_id = elem[:raceId].to_i
54
+
55
+ @guild = elem[:guild] == "" ? nil : elem[:guild]
56
+ @guild_id = elem[:guildId].to_i == 0 ? nil : elem[:guildId].to_i
57
+ @guild_url = elem[:guildUrl] == "" ? nil : elem[:guildUrl]
58
+
59
+ @realm = elem[:realm] == "" ? nil : elem[:realm]
60
+
61
+ @battle_group = elem[:battleGroup] == "" ? nil : elem[:battleGroup]
62
+ @battle_group_id = elem[:battleGroupId].to_i
63
+
64
+ @relevance = elem[:relevance].to_i
65
+ @search_rank = elem[:searchRank].to_i
66
+
67
+ # Incoming string is 2007-02-24 20:33:04.0, parse to datetime
68
+ #@last_login = elem[:lastLoginDate] == "" ? nil : DateTime.parse(elem[:lastLoginDate])
69
+ @last_login = elem[:lastLoginDate] == "" ? nil : elem[:lastLoginDate]
70
+
71
+ # From ArenaTeam info, can be blank on normal requests
72
+ #<character battleGroup="" charUrl="r=Draenor&amp;n=Lothaar" class="Paladin" classId="2"
73
+ # contribution="1602" gamesPlayed="10" gamesWon="7" gender="Male" genderId="0"
74
+ # guild="Passion" guildId="36659" guildUrl="r=Draenor&amp;n=Passion&amp;p=1" name="Lothaar"
75
+ # race="Human" raceId="1" seasonGamesPlayed="20" seasonGamesWon="13" teamRank="1"/>
76
+ @season_games_played = elem[:seasonGamesPlayed] == "" ? nil : elem[:seasonGamesPlayed].to_i
77
+ @season_games_won = elem[:seasonGamesWon] == "" ? nil : elem[:seasonGamesWon].to_i
78
+ @team_rank = elem[:teamRank] == "" ? nil : elem[:teamRank].to_i
79
+ @contribution = elem[:contribution] == "" ? nil : elem[:contribution].to_i
80
+ #@char_url = elem[:charUrl] # TODO: Merge with URL?
81
+ end
82
+
83
+
84
+ def icon(type = nil)
85
+ if !type.nil? && !@@icon_types.include?(type)
86
+ raise Wowr::Exceptions::InvalidIconType.new(@@icon_types)
87
+ end
88
+
89
+ if (type.nil?) && (@level == 70)
90
+ dir = @@icon_types[70]
91
+ elsif (type.nil?)
92
+ dir = @@icon_types[:other]
93
+ else
94
+ dir = @@icon_types[type]
95
+ end
96
+
97
+ # http://armory.worldofwarcraft.com/images/portraits/wow-70/1-7-8.gif
98
+ return base + @@portrait_url_base + dir + "/#{@gender_id}-#{@race_id}-#{@klass_id}.gif"
99
+ end
100
+
101
+
102
+ def race_icon
103
+ # http://armory.worldofwarcraft.com/images/icons/race/11-1.gif
104
+ return base + @@race_icon_url_base + "#{@race_id}-#{@gender_id.to_s}.gif"
105
+ end
106
+
107
+
108
+ def class_icon
109
+ # http://armory.worldofwarcraft.com/images/icons/class/8.gif
110
+ return base + @@class_icon_url_base + "#{@klass_id.to_s}.gif"
111
+ end
112
+
113
+
114
+ protected
115
+ def base
116
+ if @api
117
+ return @api.base_url
118
+ else
119
+ return 'http://www.wowarmory.com/'
120
+ end
121
+ end
122
+ end
123
+
124
+ class SearchCharacter < Character
125
+ end
126
+
127
+ # Character details without reputations
128
+ # uses characterInfo element
129
+ # Made up of two parts, character and charactertab
130
+ class InfoCharacter < Character
131
+
132
+ # character_info
133
+ attr_reader :char_url, :title, :known_titles,
134
+ :faction, :faction_id,
135
+ :arena_teams,
136
+ :last_modified,
137
+ :points
138
+
139
+ # character_tab
140
+ attr_reader :health, :second_bar,
141
+ :strength, :agility, :stamina, :intellect, :spirit
142
+ alias_method :str, :strength
143
+ alias_method :agi, :agility
144
+ alias_method :sta, :stamina
145
+ alias_method :int, :intellect
146
+ alias_method :spi, :spirit
147
+
148
+ attr_reader :melee, :ranged, :spell,
149
+ :defenses, :resistances,
150
+ :talent_spec, :pvp,
151
+ :professions,
152
+ :items,
153
+ :buffs, :debuffs
154
+
155
+ # It's made up of two parts
156
+ # Don't care about battlegroups yet
157
+ # I don't think I can call stuff from the constructor?
158
+ def initialize(sheet, api = nil)
159
+ @api = api
160
+
161
+ character_info(sheet%'character')
162
+
163
+ # Check if characterTab is defined. If not, the character have no infos on the armory (not logged since last armory wipe)
164
+ raise Wowr::Exceptions::CharacterNoInfo.new(@name) if (sheet%'characterTab').nil?
165
+
166
+ character_tab(sheet%'characterTab')
167
+ end
168
+
169
+ # <character
170
+ # battleGroup="Conviction"
171
+ # charUrl="r=Genjuros&amp;n=Jonlok"
172
+ # class="Warlock"
173
+ # classId="9"
174
+ # faction="Horde"
175
+ # factionId="1"
176
+ # gender="Male"
177
+ # genderId="0"
178
+ # guildName=""
179
+ # lastModified="12 February 2008"
180
+ # level="41"
181
+ # name="Jonlok"
182
+ # prefix=""
183
+ # points="2270"
184
+ # race="Orc"
185
+ # raceId="2"
186
+ # realm="Genjuros"
187
+ # suffix=""/>
188
+
189
+ def character_info(elem)
190
+ # basic info
191
+ @name = elem[:name]
192
+ @level = elem[:level].to_i
193
+ @char_url = elem[:charUrl]
194
+
195
+ @klass = elem[:class]
196
+ @klass_id = elem[:classId].to_i
197
+
198
+ @gender = elem[:gender]
199
+ @gender_id = elem[:genderId].to_i
200
+
201
+ @race = elem[:race]
202
+ @race_id = elem[:raceId].to_i
203
+
204
+ @faction = elem[:faction]
205
+ @faction_id = elem[:factionId].to_i
206
+
207
+ @guild = elem[:guildName] == "" ? nil : elem[:guildName]
208
+ @guild_url = elem[:guildUrl] == "" ? nil : elem[:guildUrl]
209
+
210
+ @prefix = elem[:prefix] == "" ? nil : elem[:prefix]
211
+ @suffix = elem[:suffix] == "" ? nil : elem[:suffix]
212
+
213
+ @points = elem[:points].to_i
214
+
215
+ @realm = elem[:realm]
216
+
217
+ @battle_group = elem[:battleGroup]
218
+
219
+ # format is February 11, 2008
220
+ # except when it's korean, and then it's 2008년 5월 11일 (일)
221
+ # tw is 2008&#24180;5&#26376;11&#26085; (2008年5月11日)
222
+ # TODO: Datetime doesn't parse other languages nicely
223
+ # Until then, just save it as a string
224
+ begin
225
+ @last_modified = elem[:lastModified] == "" ? nil : DateTime.parse(elem[:lastModified])
226
+ rescue
227
+ @last_modified = elem[:lastModified] == "" ? nil : elem[:lastModified]
228
+ end
229
+ #@last_modified = elem[:lastModified]#.to_time
230
+
231
+ @arena_teams = []
232
+ (elem/:arenaTeam).each do |arena_team|
233
+ @arena_teams << ArenaTeam.new(arena_team)
234
+ end
235
+
236
+ end
237
+
238
+ def character_tab(elem)
239
+ # <title value=""/>
240
+ @title = (elem%'title')[:value] == "" ? nil : (elem%'title')[:value]
241
+
242
+ @known_titles = []
243
+
244
+ @known_titles << @title if (@title)
245
+ (elem%'knownTitles'/:title).each do |entry|
246
+ @known_titles << entry[:value] if (!@known_titles.include?(entry[:value]))
247
+ end
248
+
249
+ @health = (elem%'characterBars'%'health')[:effective].to_i
250
+ @second_bar = SecondBar.new(elem%'characterBars'%'secondBar')
251
+
252
+ # base stats
253
+ @strength = Strength.new(elem%'baseStats'%'strength')
254
+ @agility = Agility.new(elem%'baseStats'%'agility')
255
+ @stamina = Stamina.new(elem%'baseStats'%'stamina')
256
+ @intellect = Intellect.new(elem%'baseStats'%'intellect')
257
+ @spirit = Spirit.new(elem%'baseStats'%'spirit')
258
+
259
+ # damage stuff
260
+ @melee = Melee.new(elem%'melee')
261
+ @ranged = Ranged.new(elem%'ranged')
262
+ @spell = Spell.new(elem.at(' > spell')) # TODO: hacky?
263
+ @defenses = Defenses.new(elem%'defenses')
264
+
265
+ # TODO: Massive problem, doesn't fill in resistances for some reason
266
+ resist_types = ['arcane', 'fire', 'frost', 'holy', 'nature', 'shadow']
267
+ @resistances = {}
268
+ resist_types.each do |res|
269
+ @resistances[res] = Resistance.new(elem%'resistances'%res)
270
+ end
271
+
272
+ @talent_spec = TalentSpec.new(elem%'talentSpec')
273
+
274
+ @pvp = Pvp.new(elem%'pvp')
275
+
276
+ @professions = []
277
+ (elem%'professions'/:skill).each do |skill|
278
+ @professions << Skill.new(skill)
279
+ end
280
+
281
+ @items = []
282
+ (elem%'items'/:item).each do |item|
283
+ @items << EquippedItem.new(item, @api)
284
+ end
285
+
286
+ @buffs = []
287
+ (elem%'buffs'/:spell).each do |buff|
288
+ @buffs << Buff.new(buff, @api)
289
+ end
290
+
291
+ @debuffs = []
292
+ (elem%'debuffs'/:spell).each do |debuff|
293
+ @debuffs << Buff.new(debuff, @api)
294
+ end
295
+ end
296
+ end
297
+
298
+ # Full character details with reputations
299
+ class FullCharacter < InfoCharacter
300
+ attr_reader :reputation_categories
301
+
302
+ alias_method :rep, :reputation_categories
303
+ alias_method :reputation, :reputation_categories
304
+
305
+ def initialize(sheet, reputation, api = nil)
306
+ @api = api
307
+
308
+ # Build the InfoCharacter
309
+ super(sheet, api)
310
+
311
+ # Add reputations
312
+ character_reputation(reputation)
313
+ end
314
+
315
+ # character-reputation.xml
316
+ def character_reputation(elem)
317
+ @reputation_categories = {}
318
+ (elem/:factionCategory).each do |category|
319
+ @reputation_categories[category[:key]] = RepFactionCategory.new(category)
320
+ end
321
+ end
322
+ end
323
+
324
+
325
+ # Second stat bar, depends on character class
326
+ class SecondBar
327
+ attr_reader :effective, :casting, :not_casting, :type
328
+
329
+ def initialize(elem)
330
+ @effective = elem[:effective].to_i
331
+ @casting = elem[:casting].to_i == -1 ? nil : elem[:casting].to_i
332
+ @not_casting = elem[:notCasting].to_i == -1 ? nil : elem[:notCasting].to_i
333
+ @type = elem[:type]
334
+ end
335
+ end
336
+
337
+
338
+ class BaseStat # abstract?
339
+ attr_reader :base, :effective
340
+ end
341
+
342
+ class Strength < BaseStat
343
+ attr_reader :attack, :block
344
+ def initialize(elem)
345
+ @base = elem['base'].to_i
346
+ @effective = elem['effective'].to_i
347
+ @attack = elem['attack'].to_i
348
+ @block = elem['block'].to_i == -1 ? nil : elem['block'].to_i
349
+ end
350
+ end
351
+
352
+ class Agility < BaseStat
353
+ attr_reader :armor, :attack, :crit_hit_percent
354
+ def initialize(elem)
355
+ @base = elem[:base].to_i
356
+ @effective = elem[:effective].to_i
357
+ @armor = elem[:armor].to_i
358
+ @attack = elem[:attack].to_i == -1 ? nil : elem[:attack].to_i
359
+ @crit_hit_percent = elem[:critHitPercent].to_f
360
+ end
361
+ end
362
+
363
+ class Stamina < BaseStat
364
+ attr_reader :health, :pet_bonus
365
+ def initialize(elem)
366
+ @base = elem[:base].to_i
367
+ @effective = elem[:effective].to_i
368
+ @health = elem[:health].to_i
369
+ @pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
370
+ end
371
+ end
372
+
373
+ class Intellect < BaseStat
374
+ attr_reader :mana, :crit_hit_percent, :pet_bonus
375
+ def initialize(elem)
376
+ @base = elem[:base].to_i
377
+ @effective = elem[:effective].to_i
378
+ @mana = elem[:mana].to_i
379
+ @crit_hit_percent = elem[:critHitPercent].to_f
380
+ @pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
381
+ end
382
+ end
383
+
384
+ class Spirit < BaseStat
385
+ attr_reader :health_regen, :mana_regen
386
+ def initialize(elem)
387
+ @base = elem[:base].to_i
388
+ @effective = elem[:effective].to_i
389
+ @health_regen = elem[:healthRegen].to_i
390
+ @mana_regen = elem[:manaRegen].to_i
391
+ end
392
+ end
393
+
394
+ class Armor < BaseStat
395
+ attr_reader :percent, :pet_bonus
396
+ def initialize(elem)
397
+ @base = elem[:base].to_i
398
+ @effective = elem[:effective].to_i
399
+ @percent = elem[:percent].to_f
400
+ @pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
401
+ end
402
+ end
403
+
404
+
405
+
406
+ # <melee>
407
+ # <mainHandDamage dps="65.6" max="149" min="60" percent="0" speed="1.60"/>
408
+ # <offHandDamage dps="0.0" max="0" min="0" percent="0" speed="2.00"/>
409
+ # <mainHandSpeed hastePercent="0.00" hasteRating="0" value="1.60"/>
410
+ # <offHandSpeed hastePercent="0.00" hasteRating="0" value="2.00"/>
411
+ # <power base="338" effective="338" increasedDps="24.0"/>
412
+ # <hitRating increasedHitPercent="0.00" value="0"/>
413
+ # <critChance percent="4.16" plusPercent="0.00" rating="0"/>
414
+ # <expertise additional="0" percent="0.00" rating="0" value="0"/>
415
+ # </melee>
416
+ class Melee
417
+ attr_reader :main_hand_skill, :off_hand_skill,
418
+ :main_hand_damage, :off_hand_damage,
419
+ :main_hand_speed, :off_hand_speed,
420
+ :power, :hit_rating, :crit_chance,
421
+ :expertise
422
+
423
+ def initialize(elem)
424
+ # TODO: Do these not exist anymore?
425
+ @main_hand_skill = WeaponSkill.new(elem%'mainHandWeaponSkill') if (elem%'mainHandWeaponSkill')
426
+ @off_hand_skill = WeaponSkill.new(elem%'offHandWeaponSkill') if (elem%'offHandWeaponSkill')
427
+
428
+ @main_hand_damage = WeaponDamage.new(elem%'mainHandDamage')
429
+ @off_hand_damage = WeaponDamage.new(elem%'offHandDamage')
430
+
431
+ @main_hand_speed = WeaponSpeed.new(elem%'mainHandSpeed')
432
+ @off_hand_speed = WeaponSpeed.new(elem%'offHandSpeed')
433
+
434
+ @power = WeaponPower.new(elem%'power')
435
+ @hit_rating = WeaponHitRating.new(elem%'hitRating')
436
+ @crit_chance = WeaponCritChance.new(elem%'critChance')
437
+
438
+ @expertise = WeaponExpertise.new(elem%'expertise')
439
+ end
440
+ end
441
+
442
+ # <ranged>
443
+ # <weaponSkill rating="0" value="-1"/>
444
+ # <damage dps="0.0" max="0" min="0" percent="0" speed="0.00"/>
445
+ # <speed hastePercent="0.00" hasteRating="0" value="0.00"/>
446
+ # <power base="57" effective="57" increasedDps="4.0" petAttack="-1.00" petSpell="-1.00"/>
447
+ # <hitRating increasedHitPercent="0.00" value="0"/>
448
+ # <critChance percent="0.92" plusPercent="0.00" rating="0"/>
449
+ # </ranged>
450
+ class Ranged
451
+ attr_reader :weapon_skill, :damage, :speed, :power,
452
+ :hit_rating, :crit_chance
453
+
454
+ def initialize(elem)
455
+ @weapon_skill = WeaponSkill.new(elem%'weaponSkill')
456
+ @damage = WeaponDamage.new(elem%'damage')
457
+ @speed = WeaponSpeed.new(elem%'speed')
458
+ @power = WeaponPower.new(elem%'power')
459
+ @hit_rating = WeaponHitRating.new(elem%'hitRating')
460
+ @crit_chance = WeaponCritChance.new(elem%'critChance')
461
+ end
462
+ end
463
+
464
+ class WeaponSkill
465
+ attr_reader :rating, :value
466
+
467
+ def initialize(elem)
468
+ @value = elem[:value].to_i == -1 ? nil : elem[:value].to_i
469
+ @rating = elem[:rating].to_i
470
+ end
471
+ end
472
+
473
+ class WeaponDamage
474
+ attr_reader :dps, :max, :min, :percent, :speed
475
+
476
+ def initialize(elem)
477
+ @dps = elem[:dps].to_f
478
+ @max = elem[:max].to_i
479
+ @min = elem[:min].to_i
480
+ @percent = elem[:percent].to_f
481
+ @speed = elem[:speed].to_f
482
+ end
483
+ end
484
+
485
+ class WeaponSpeed
486
+ attr_reader :haste_percent, :haste_rating, :value
487
+
488
+ def initialize(elem)
489
+ @haste_percent = elem[:hastePercent].to_f
490
+ @haste_rating = elem[:hasteRating].to_f
491
+ @value = elem[:value].to_f
492
+ end
493
+ end
494
+
495
+ class WeaponPower
496
+ attr_reader :base, :effective, :increased_dps, :pet_attack, :pet_spell, :haste_rating
497
+
498
+ def initialize(elem)
499
+ @base = elem[:base].to_i
500
+ @haste_rating = elem[:effective].to_i
501
+ @increased_dps = elem[:increasedDps].to_f
502
+ @pet_attack = (elem[:petAttack].to_f == -1 ? nil : elem[:petAttack].to_f)
503
+ @pet_spell = (elem[:petSpell].to_f == -1 ? nil : elem[:petSpell].to_f)
504
+ end
505
+ end
506
+
507
+ class WeaponHitRating
508
+ attr_reader :increased_hit_percent, :value
509
+
510
+ def initialize(elem)
511
+ @increased_hit_percent = elem[:increasedHitPercent].to_f
512
+ @value = elem[:value].to_f
513
+ end
514
+ end
515
+
516
+ class WeaponCritChance
517
+ attr_reader :percent, :plus_percent, :rating
518
+
519
+ def initialize(elem)
520
+ @percent = elem[:percent].to_f
521
+ @plus_percent = elem[:plusPercent].to_f
522
+ @rating = elem[:rating].to_i
523
+ end
524
+ end
525
+
526
+ # <expertise additional="0" percent="0.00" rating="0" value="0"/>
527
+ class WeaponExpertise
528
+ attr_reader :additional, :percent, :rating, :value
529
+
530
+ def initialize(elem)
531
+ @additional = elem[:additional].to_i
532
+ @percent = elem[:percent].to_f
533
+ @rating = elem[:rating].to_i
534
+ @value = elem[:value].to_i
535
+ end
536
+ end
537
+
538
+
539
+ # Decided to do funky stuff to the XML to make it more useful.
540
+ # instead of having two seperate lists of bonusDamage and critChance
541
+ # merged it into one set of objects for each thing
542
+ class Spell
543
+ attr_reader :arcane, :fire, :frost, :holy, :nature, :shadow,
544
+ :hit_rating, :bonus_healing, :penetration, :mana_regen, :speed
545
+
546
+ def initialize(elem)
547
+ @arcane = SpellDamage.new(elem%'bonusDamage'%'arcane', elem%'critChance'%'arcane')
548
+ @fire = SpellDamage.new(elem%'bonusDamage'%'fire', elem%'critChance'%'fire')
549
+ @frost = SpellDamage.new(elem%'bonusDamage'%'frost', elem%'critChance'%'frost')
550
+ @holy = SpellDamage.new(elem%'bonusDamage'%'holy', elem%'critChance'%'holy')
551
+ @nature = SpellDamage.new(elem%'bonusDamage'%'nature', elem%'critChance'%'nature')
552
+ @shadow = SpellDamage.new(elem%'bonusDamage'%'shadow', elem%'critChance'%'shadow')
553
+
554
+ @bonus_healing = (elem%'bonusHealing')[:value].to_i # is this right??
555
+ @penetration = (elem%'penetration')[:value].to_i
556
+ @hit_rating = WeaponHitRating.new(elem%'hitRating')
557
+ @mana_regen = ManaRegen.new(elem%'manaRegen')
558
+ @speed = SpellSpeed.new(elem%'hasteRating')
559
+
560
+ # elements = %w[arcane fire frost holy nature shadow]
561
+ # elements.each do |element|
562
+ # # TODO: is this a good idea?
563
+ # #instance_variable_set("@#{element}", foo) #??
564
+ # #eval("@#{element} = SpellDamage.new(elem[:bonusDamage][element][:value], elem[:critChance][element][:percent]).to_f)")
565
+ # # eval("@#{element} = SpellDamage.new((elem%'bonusDamage'%element)[:value].to_i,
566
+ # # (elem%'critChance'%element)[:percent].to_f)")
567
+ # end
568
+ end
569
+ end
570
+
571
+ class SpellSpeed
572
+ attr_reader :percent_increase, :haste_rating
573
+
574
+ def initialize(elem)
575
+ @percent_increase = elem[:hastePercent].to_f
576
+ @haste_rating = elem[:hasteRating].to_i
577
+ end
578
+ end
579
+
580
+ class SpellDamage
581
+ attr_reader :value, :crit_chance_percent
582
+ alias_method :percent, :crit_chance_percent
583
+
584
+ def initialize(bonusDamage_elem, critChance_elem)
585
+ @value = bonusDamage_elem[:value].to_i
586
+ @crit_chance_percent = critChance_elem[:percent].to_f
587
+ end
588
+ end
589
+
590
+ class ManaRegen
591
+ attr_reader :casting, :not_casting
592
+
593
+ def initialize(elem)
594
+ @casting = elem[:casting].to_f
595
+ @not_casting = elem[:notCasting].to_f
596
+ end
597
+ end
598
+
599
+ class PetBonus
600
+ attr_reader :attack, :damage, :from_Type
601
+
602
+ def initialize(elem)
603
+ @attack = elem[:attack].to_i == -1 ? nil : elem[:attack].to_i
604
+ @damage = elem[:damage].to_i == -1 ? nil : elem[:damage].to_i
605
+ @from_type = elem[:fromType] if elem[:fromType]
606
+ end
607
+ end
608
+
609
+
610
+
611
+ class Defenses
612
+ attr_reader :armor, :defense, :dodge, :parry, :block, :resilience
613
+
614
+ def initialize(elem)
615
+ @armor = Armor.new(elem%'armor')
616
+ @defense = Defense.new(elem%'defense')
617
+ @dodge = DodgeParryBlock.new(elem%'dodge')
618
+ @parry = DodgeParryBlock.new(elem%'parry')
619
+ @block = DodgeParryBlock.new(elem%'block')
620
+ @resilience = Resilience.new(elem%'resilience')
621
+ end
622
+ end
623
+
624
+ class Armor
625
+ attr_reader :base, :effective, :percent, :pet_bonus
626
+
627
+ def initialize(elem)
628
+ @base = elem[:base].to_i
629
+ @effective = elem[:effective].to_i
630
+ @percent = elem[:percent].to_f
631
+ @pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
632
+ end
633
+ end
634
+
635
+ class Defense
636
+ attr_reader :value, :increase_percent, :decrease_percent, :plus_defense, :rating
637
+
638
+ def initialize(elem)
639
+ @value = elem[:value].to_i
640
+ @increase_percent = elem[:increasePercent].to_f
641
+ @decrease_percent = elem[:decreasePercent].to_f
642
+ @plus_defense = elem[:plusDefense].to_i
643
+ @rating = elem[:rating].to_i
644
+ end
645
+ end
646
+
647
+ class DodgeParryBlock
648
+ attr_reader :percent, :increase_percent, :rating
649
+
650
+ def initialize(elem)
651
+ @percent = elem[:percent].to_f
652
+ @increase_percent = elem[:increasePercent].to_f
653
+ @rating = elem[:rating].to_i
654
+ end
655
+ end
656
+
657
+ class Resilience
658
+ attr_reader :damage_percent, :hit_percent, :value
659
+
660
+ def initialize(elem)
661
+ @damage_percent = elem[:damagePercent].to_f
662
+ @hit_percent = elem[:hitPercent].to_f
663
+ @value = elem[:value].to_f
664
+ end
665
+ end
666
+
667
+
668
+ class Resistance
669
+ attr_reader :value, :pet_bonus
670
+
671
+ def initialize(elem)
672
+ @value = elem[:value].to_i
673
+ @pet_bonus = elem[:petBonus].to_i == -1 ? nil : elem[:petBonus].to_i
674
+ end
675
+ end
676
+
677
+
678
+ # Note the list of talent trees starts at 1. This is quirky, but that's what's used in the XML
679
+ class TalentSpec
680
+ attr_reader :trees
681
+
682
+ def initialize(elem)
683
+ @trees = []
684
+ @trees[1] = elem[:treeOne].to_i
685
+ @trees[2] = elem[:treeTwo].to_i
686
+ @trees[3] = elem[:treeThree].to_i
687
+ end
688
+ end
689
+
690
+
691
+ # Player-versus-player data
692
+ class Pvp
693
+ attr_reader :lifetime_honorable_kills, :arena_currency
694
+
695
+ def initialize(elem)
696
+ @lifetime_honorable_kills = (elem%'lifetimehonorablekills')[:value].to_i
697
+ @arena_currency = (elem%'arenacurrency')[:value].to_i
698
+ end
699
+ end
700
+
701
+
702
+ # A buff
703
+ # TODO: Code duplication, see basic Item class. Make extend Icon class?
704
+ class Buff
705
+ attr_reader :name, :effect, :icon_base
706
+ alias_method :to_s, :name
707
+
708
+ @@icon_url_base = 'images/icons/'
709
+ @@icon_sizes = {:large => ['64x64', 'jpg'], :medium => ['43x43', 'png'], :small => ['21x21', 'png']}
710
+
711
+ def initialize(elem, api = nil)
712
+ @api = api
713
+
714
+ @name = elem[:name]
715
+ @effect = elem[:effect]
716
+ @icon_base = elem[:icon]
717
+ end
718
+
719
+ # http://armory.worldofwarcraft.com/images/icons/21x21/spell_holy_arcaneintellect.png
720
+ def icon(size = :medium)
721
+ if !@@icon_sizes.include?(size)
722
+ raise Wowr::Exceptions::InvalidIconSize.new(@@icon_sizes)
723
+ end
724
+
725
+ if @api
726
+ base = @api.base_url
727
+ else
728
+ base = 'http://www.wowarmory.com/'
729
+ end
730
+
731
+ # http://www.wowarmory.com/images/icons/64x64/blahblah.jpg
732
+ return base + @@icon_url_base + @@icon_sizes[size][0] + '/' + @icon_base + '.' + @@icon_sizes[size][1]
733
+ end
734
+ end
735
+
736
+
737
+ # An item equipped to a player
738
+ class EquippedItem < Item
739
+ attr_reader :durability, :max_durability, #:id, :item_id, :icon,
740
+ :gems, :permanent_enchant,
741
+ :random_properties_id, :seed, :slot
742
+
743
+ def initialize(elem, api = nil)
744
+ super(elem, api)
745
+ @durability = elem[:durability].to_i
746
+ @max_durability = elem[:maxDurability].to_i
747
+ @gems = []
748
+ @gems[0] = elem[:gem0Id].to_i == 0 ? nil : elem[:gem0Id].to_i
749
+ @gems[1] = elem[:gem1Id].to_i == 0 ? nil : elem[:gem1Id].to_i
750
+ @gems[2] = elem[:gem2Id].to_i == 0 ? nil : elem[:gem2Id].to_i
751
+ @permanent_enchant = elem[:permanentenchant].to_i
752
+ @random_properties_id = elem[:randomPropertiesId] == 0 ? nil : elem[:randomPropertiesId].to_i
753
+ @seed = elem[:seed].to_i # not sure if seed is so big it's overloading
754
+ @slot = elem[:slot].to_i
755
+ end
756
+ end
757
+
758
+
759
+ # eg Daggers, Riding, Fishing, language
760
+ class Skill
761
+ attr_reader :key, :name, :value, :max
762
+ alias_method :to_s, :name
763
+ alias_method :to_i, :value
764
+
765
+ def initialize(elem)
766
+ @key = elem[:key]
767
+ @name = elem[:name]
768
+ @value = elem[:value].to_i
769
+ @max = elem[:max].to_i
770
+ end
771
+ end
772
+
773
+
774
+ # Larger group of factions
775
+ # Used for faction information
776
+ # eg Alliance, Shattrath City, Steamwheedle Cartel
777
+ class RepFactionCategory
778
+ attr_reader :key, :name, :factions
779
+ alias_method :to_s, :name
780
+
781
+ def initialize(elem)
782
+ @key = elem[:key]
783
+ @name = elem[:name]
784
+
785
+ @factions = {}
786
+ (elem/:faction).each do |faction|
787
+ @factions[faction[:key]] = RepFaction.new(faction)
788
+ end
789
+ end
790
+
791
+ def total
792
+ total = 0
793
+ factions.each_value { |faction| total += faction.reputation }
794
+ return total
795
+ end
796
+ end
797
+
798
+
799
+ # Smaller NPC faction that is part of a FactionCategory
800
+ # eg Darnassus, Argent Dawn
801
+ class RepFaction
802
+ attr_reader :key, :name, :reputation
803
+ alias_method :to_s, :name
804
+ alias_method :to_i, :reputation
805
+
806
+ alias_method :rep, :reputation
807
+
808
+ def initialize(elem)
809
+ @key = elem[:key]
810
+ @name = elem[:name]
811
+ @reputation = elem[:reputation].to_i
812
+ end
813
+ end
814
+
815
+ end
816
+ end