gatherer_set_parser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gatherer_set_parser.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 David Ratajczak
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # GathererSetParser
2
+
3
+ This is a tool for parsing the official Magic the Gathering card database for
4
+ a complete collection of cards belonging to a specific set. The cards of the
5
+ given set will be returned in an array of hashes.
6
+
7
+ This gem was extracted from a personal project, and hasn't been throughly
8
+ tested for every set in production. You'll likely want to refactor a lot of
9
+ the code before using it on a production server.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'gatherer_set_parser'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install gatherer_set_parser
24
+
25
+ ## Usage
26
+
27
+ This set requires you to pass in a case sensitive set name from the Gatherer
28
+ website. Here's an example:
29
+
30
+ ```
31
+ # Create the array of cards from the given set name
32
+ cards = GathererSetParser::Fetch.new('Dark Ascension').cards
33
+ ```
34
+
35
+ This will return an array of 171 cards. Here's an example of the output of
36
+ cards.first:
37
+
38
+ ```
39
+ { :title=> "Afflicted Deserter",
40
+ :card_type => "Creature",
41
+ :sub_type => "",
42
+ :power => 3,
43
+ :toughness => 2,
44
+ :cmc => 4,
45
+ :mana_cost => ["3", "R"],
46
+ :loyalty => nil,
47
+ :image_url => "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=262675&type=card",
48
+ :text => "\r\n At the beginning of each upkeep, if no spells were cast last turn, transform Afflicted Deserter.",
49
+ :gatherer_url => "http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=262675",
50
+ :rarity => "Uncommon" }
51
+ ```
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create new Pull Request
60
+
61
+ ## Contact
62
+
63
+ [david@mockra.com](mailto:david@mockra.com)
64
+
65
+ [@Mockra_](http://twitter.com/#!/mockra_)
66
+
67
+ [mockra.com](http://mockra.com)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/gatherer_set_parser/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["David Ratajczak"]
6
+ gem.email = ["david@mockra.com"]
7
+ gem.description = %q{Parses the MTG Gatherer website for a collection of
8
+ cards from the provided set.}
9
+ gem.summary = %q{This gem will return an array of card hashes that you
10
+ can use to build a database of Magic the Gathering cards.}
11
+ gem.homepage = "https://github.com/mockra/gatherer_set_parser"
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.name = "gatherer_set_parser"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = GathererSetParser::VERSION
19
+
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec'
22
+ gem.add_development_dependency 'fakeweb'
23
+ gem.add_development_dependency 'vcr'
24
+ gem.add_dependency 'nokogiri'
25
+ end
@@ -0,0 +1,114 @@
1
+ module GathererSetParser
2
+ class CardParser
3
+
4
+ attr_accessor :body
5
+
6
+ def initialize card
7
+ @body = card
8
+ end
9
+
10
+ def attributes
11
+ { title: title,
12
+ card_type: type,
13
+ sub_type: sub_type,
14
+ power: power,
15
+ toughness: toughness,
16
+ cmc: cmc,
17
+ mana_cost: mana_cost,
18
+ loyalty: loyalty,
19
+ image_url: image_url,
20
+ text: text,
21
+ gatherer_url: gatherer_url,
22
+ rarity: rarity }
23
+ end
24
+
25
+ def title
26
+ body.css('.cardTitle > a').text
27
+ end
28
+
29
+ def type_box
30
+ body.css('span.typeLine').text.
31
+ gsub(/\n/, '').gsub(/[ ]{2,}/, ' ').strip.lstrip
32
+ end
33
+
34
+ def type
35
+ type_box.match(/[a-z0-9 ]*/i).to_s.strip
36
+ end
37
+
38
+ def sub_type
39
+ type_box.gsub(/\(.*\)/, '').
40
+ match(/ ([a-z0-9 ]*)\z/i)[1].to_s.lstrip.strip
41
+ rescue
42
+ nil
43
+ end
44
+
45
+ def power
46
+ type_box.match(/\(([0-9]*)\//)[1].to_i rescue nil
47
+ end
48
+
49
+ def toughness
50
+ type_box.match(/\/([0-9]*)\)/)[1].to_i rescue nil
51
+ end
52
+
53
+ def cmc
54
+ body.css('.convertedManaCost').text.to_i
55
+ end
56
+
57
+ def loyalty
58
+ type_box.match(/\(([0-9]*)\)/)[1].to_i rescue nil
59
+ end
60
+
61
+ def rarity
62
+ body.css('.rightCol > a').first.children.attribute('alt').text.
63
+ match(/\((.*)\)/)[1].to_s
64
+ end
65
+
66
+ def text
67
+ body.xpath('//div[@class="rulesText"]//img[@alt]').each{ |i| i.swap( i['alt'].parse_mana_cost ) }
68
+ body.css('.rulesText').text.gsub(/[ ]{2,}/, ' ')
69
+ end
70
+
71
+ def mana_cost
72
+ array = []
73
+ body.css('.manaCost >img').each do |img|
74
+ array << img.attribute('alt').text.parse_mana_cost
75
+ end
76
+ array
77
+ end
78
+
79
+ def image_url
80
+ 'http://gatherer.wizards.com/' +
81
+ body.css('.leftCol > a > img').attribute('src').
82
+ text.gsub(/\.\.\//, '')
83
+ end
84
+
85
+ def gatherer_url
86
+ 'http://gatherer.wizards.com/Pages/' +
87
+ body.css('.cardTitle > a').attribute('href').
88
+ text.gsub(/\.\.\//, '')
89
+ end
90
+
91
+ end
92
+ end
93
+
94
+ class String
95
+ def parse_mana_cost
96
+ extract_phyrexia.extract_colors.extract_unique_costs
97
+ end
98
+
99
+ def extract_phyrexia
100
+ gsub(/Phyrexian Blue/, 'U/P').gsub(/Phyrexian Black/, 'B/P').
101
+ gsub(/Phyrexian White/, 'W/P').gsub(/Phyrexian Red/, 'R/P').
102
+ gsub(/Phyrexian Green/, 'G/P')
103
+ end
104
+
105
+ def extract_colors
106
+ gsub(/Blue/, 'U').gsub(/Black/, 'B').gsub(/Red/, 'R').gsub(/Green/, 'G').
107
+ gsub(/White/, 'W')
108
+ end
109
+
110
+ def extract_unique_costs
111
+ gsub(/Variable Colorless/, 'X').gsub(/Two/, '2').gsub(/or/, '/').
112
+ gsub(/Snow/, 'S').gsub(/Tap/, 'T').gsub(/Untap/, 'Q')
113
+ end
114
+ end
@@ -0,0 +1,46 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+
4
+ module GathererSetParser
5
+ class Fetch
6
+
7
+ include GathererSetParser::QueryString
8
+
9
+ attr_reader :set_name
10
+
11
+ def response_page page
12
+ Nokogiri::HTML open(gatherer_url page), nil, 'UTF-8'
13
+ end
14
+
15
+ def complete_set
16
+ @complete_set ||= gather_set
17
+ end
18
+
19
+ def gather_set
20
+ set, page = [], 0
21
+ current_page = response_page page
22
+ total_pages = current_page.css('.paging > a').count
23
+ begin
24
+ set << current_page
25
+ page += 1
26
+ current_page = response_page page
27
+ end while page < total_pages - 1
28
+ set
29
+ end
30
+
31
+ def cards
32
+ @cards ||= collect_cards
33
+ end
34
+
35
+ def collect_cards
36
+ collection = []
37
+ complete_set.each do |page|
38
+ page.css('.cardItemTable > tr > td > table').each do |card|
39
+ collection << CardParser.new(card).attributes
40
+ end
41
+ end
42
+ collection
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ module GathererSetParser
2
+ module QueryString
3
+
4
+ def initialize set
5
+ @set_name = set
6
+ end
7
+
8
+ def escaped_set
9
+ @query_string ||= URI.escape set_name
10
+ end
11
+
12
+ def query_string
13
+ "output=standard&set=[%22#{escaped_set}%22]"
14
+ end
15
+
16
+ def url
17
+ "http://gatherer.wizards.com/Pages/Search/Default.aspx?"
18
+ end
19
+
20
+ def gatherer_url page
21
+ url + query_string + "&page=#{page}"
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module GathererSetParser
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "gatherer_set_parser/version"
2
+ require 'gatherer_set_parser/query_string'
3
+ require 'gatherer_set_parser/card_parser'
4
+ require 'gatherer_set_parser/fetch'
5
+
6
+ module GathererSetParser
7
+ end
@@ -0,0 +1,181 @@
1
+ require 'gatherer_set_parser_spec'
2
+ require 'fakeweb'
3
+
4
+ describe GathererSetParser::CardParser do
5
+
6
+ let(:parsed_card) { GathererSetParser::CardParser.new card }
7
+
8
+ describe '#parse_card' do
9
+ let(:card) { Nokogiri::HTML open('spec/support/flipped_creature.html') }
10
+
11
+ it 'should return a hash' do
12
+ parsed_card.attributes.should be_a Hash
13
+ end
14
+
15
+ it 'should have a title' do
16
+ parsed_card.attributes[:title].should_not be_nil
17
+ end
18
+ end
19
+
20
+ describe '#card_attributes' do
21
+ context 'back of flip card' do
22
+ let(:card) { Nokogiri::HTML open('spec/support/flipped_creature.html') }
23
+
24
+ it 'should return the correct title' do
25
+ parsed_card.title.should == "Tovolar's Magehunter"
26
+ end
27
+
28
+ it 'should return a card type' do
29
+ parsed_card.type.should == 'Creature'
30
+ end
31
+
32
+ it 'should match the species type' do
33
+ parsed_card.sub_type.should == 'Werewolf'
34
+ end
35
+
36
+ it 'should have the correct power' do
37
+ parsed_card.power.should == 5
38
+ end
39
+
40
+ it 'should have the correct toughness' do
41
+ parsed_card.toughness.should == 5
42
+ end
43
+
44
+ it 'should have no loyalty' do
45
+ parsed_card.loyalty.should be_nil
46
+ end
47
+
48
+ it 'should have a cmc' do
49
+ parsed_card.cmc.should == 0
50
+ end
51
+
52
+ it 'should have the correct rarity' do
53
+ parsed_card.rarity.should == 'Rare'
54
+ end
55
+
56
+ it 'should have text' do
57
+ parsed_card.text.should =~ /transform/i
58
+ end
59
+
60
+ it 'should have the correct mana cost' do
61
+ parsed_card.mana_cost.should == []
62
+ end
63
+
64
+ it 'should have the correct image url' do
65
+ parsed_card.image_url.should =~
66
+ /\/Handlers\/Image\.ashx\?multiverseid\=253429\&type\=card/
67
+ end
68
+ end
69
+
70
+ context 'an instant/sorcery' do
71
+ let(:card) { Nokogiri::HTML open('spec/support/instant.html') }
72
+
73
+ it 'should have no power' do
74
+ parsed_card.power.should be_nil
75
+ end
76
+
77
+ it 'should have no loyalty' do
78
+ parsed_card.loyalty.should be_nil
79
+ end
80
+
81
+ it 'should have the correct type' do
82
+ parsed_card.type.should == 'Instant'
83
+ end
84
+
85
+ it 'should have the correct rarity' do
86
+ parsed_card.rarity.should == 'Uncommon'
87
+ end
88
+
89
+ it 'should have the correct mana cost' do
90
+ parsed_card.mana_cost.should == %w[1 U]
91
+ end
92
+
93
+ it 'should have no sub type' do
94
+ parsed_card.sub_type.should be_nil
95
+ end
96
+ end
97
+
98
+ context 'land' do
99
+ let(:card) { Nokogiri::HTML open('spec/support/land.html') }
100
+
101
+ it 'should have the correct mana cost' do
102
+ parsed_card.mana_cost.should == []
103
+ end
104
+
105
+ it 'should have no power' do
106
+ parsed_card.power.should be_nil
107
+ end
108
+ end
109
+
110
+ context 'planeswalker' do
111
+ let(:card) { Nokogiri::HTML open('spec/support/planeswalker.html') }
112
+
113
+ it 'should have a title' do
114
+ parsed_card.title.should =~ /Tamiyo/i
115
+ end
116
+
117
+ it 'should have no power' do
118
+ parsed_card.power.should be_nil
119
+ end
120
+
121
+ it 'should have no toughness' do
122
+ parsed_card.power.should be_nil
123
+ end
124
+
125
+ it 'should have loyalty' do
126
+ parsed_card.loyalty.should == 4
127
+ end
128
+
129
+ it 'should have the correct type' do
130
+ parsed_card.type.should == 'Planeswalker'
131
+ end
132
+
133
+ it 'should have the correct rarity' do
134
+ parsed_card.rarity.should == 'Mythic Rare'
135
+ end
136
+
137
+ it 'should have the correct mana cost' do
138
+ parsed_card.mana_cost.should == %w[3 U U]
139
+ end
140
+
141
+ it 'should have correct gatherer url' do
142
+ parsed_card.gatherer_url.should =~
143
+ /Card\/Details\.aspx\?multiverseid\=240070/
144
+ end
145
+ end
146
+
147
+ context 'phyrexian' do
148
+ let(:card) { Nokogiri::HTML open('spec/support/phyrexia.html') }
149
+
150
+ it 'should have the correct mana cost' do
151
+ parsed_card.mana_cost.should == %w[X G/P]
152
+ end
153
+ end
154
+
155
+ context 'enchant' do
156
+ let(:card) { Nokogiri::HTML open('spec/support/enchant.html') }
157
+
158
+ it 'should have the correct mana cost' do
159
+ parsed_card.mana_cost.should == %w[4 B B]
160
+ end
161
+ end
162
+
163
+ context 'gold' do
164
+ end
165
+
166
+ context 'dual_land' do
167
+ let(:card) { Nokogiri::HTML open('spec/support/dual_land.html') }
168
+
169
+ it 'should have the correct text' do
170
+ parsed_card.text.should =~ /U/i
171
+ end
172
+ end
173
+
174
+ context 'legendary' do
175
+ end
176
+
177
+ context 'artifact' do
178
+ end
179
+ end
180
+
181
+ end
@@ -0,0 +1,30 @@
1
+ require 'gatherer_set_parser_spec'
2
+
3
+ describe GathererSetParser::Fetch do
4
+
5
+ let(:gatherer) { GathererSetParser::Fetch.new 'Dark Ascension' }
6
+
7
+ describe '.new' do
8
+ it 'should assign the set_name' do
9
+ gatherer.set_name.should == 'Dark Ascension'
10
+ end
11
+ end
12
+
13
+ describe '#response_body' do
14
+ use_vcr_cassette 'dark_ascension'
15
+
16
+ it 'should contain every page' do
17
+ gatherer.complete_set.count.should == 7
18
+ end
19
+
20
+ it 'should contain a magic card' do
21
+ gatherer.complete_set.last.text.should =~ /wakedancer/i
22
+ end
23
+
24
+ it 'should have the complete set of cards' do
25
+ gatherer.cards.count.should == 171
26
+ end
27
+ end
28
+
29
+ end
30
+
@@ -0,0 +1,21 @@
1
+ require 'gatherer_set_parser_spec'
2
+
3
+ describe GathererSetParser::QueryString do
4
+
5
+ let(:gatherer) { GathererSetParser::Fetch.new 'Dark Ascension' }
6
+
7
+ describe '#escaped_set' do
8
+ it 'should escape set_name to query string' do
9
+ gatherer.escaped_set.should == 'Dark%20Ascension'
10
+ end
11
+ end
12
+
13
+ describe '#query_string' do
14
+ it 'should contain the entire query' do
15
+ gatherer.query_string.should ==
16
+ 'output=standard&set=[%22Dark%20Ascension%22]'
17
+ end
18
+ end
19
+
20
+ end
21
+
@@ -0,0 +1,2 @@
1
+ require 'gatherer_set_parser'
2
+ require 'vcr_setup'
@@ -0,0 +1,35 @@
1
+ <table>
2
+ <tr class="cardItem evenItem">
3
+ <td class="leftCol">
4
+ <div class="clear"></div>
5
+ <a href="../Card/Details.aspx?multiverseid=230070" id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl00_cardImageLink" onclick="return CardLinkAction(event, this, 'SameWindow');">
6
+ <img src="../../Handlers/Image.ashx?multiverseid=230070&amp;type=card" id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl00_cardImage" width="95" height="132" alt="Conversion Chamber" border="0" />
7
+ </a>
8
+ <div class="clear"></div>
9
+ </td>
10
+ <td class="middleCol">
11
+ <div class="clear"></div>
12
+ <div class="cardInfo">
13
+ <span class="cardTitle">
14
+ <a id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl00_cardTitle" onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=230070">Conversion Chamber</a></span> <span class="manaCost">
15
+ <img src="/Handlers/Image.ashx?size=small&amp;name=3&amp;type=symbol" alt="3" align="absbottom" /></span> (<span class="convertedManaCost">3</span>)
16
+ <br />
17
+ <span class="typeLine">
18
+ Artifact
19
+ </span>
20
+ <br />
21
+ <div class="rulesText">
22
+ <p><img src="/Handlers/Image.ashx?size=small&amp;name=2&amp;type=symbol" alt="2" align="absbottom" />, <img src="/Handlers/Image.ashx?size=small&amp;name=tap&amp;type=symbol" alt="Tap" align="absbottom" />: Exile target artifact card from a graveyard. Put a charge counter on Conversion Chamber.</p><p><img src="/Handlers/Image.ashx?size=small&amp;name=2&amp;type=symbol" alt="2" align="absbottom" />, <img src="/Handlers/Image.ashx?size=small&amp;name=tap&amp;type=symbol" alt="Tap" align="absbottom" />, Remove a charge counter from Conversion Chamber: Put a 3/3 colorless Golem artifact creature token onto the battlefield.</p></div>
23
+ </div>
24
+ </td>
25
+ <td class="rightCol setVersions">
26
+ <div class="clear"></div>
27
+ <div>
28
+ <div id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl00_cardSetCurrent" class="rightCol">
29
+ <a onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=230070"><img title="New Phyrexia (Uncommon)" src="../../Handlers/Image.ashx?type=symbol&amp;set=NPH&amp;size=small&amp;rarity=U" alt="New Phyrexia (Uncommon)" style="border-width:0px;" /></a>
30
+ </div>
31
+ </div>
32
+
33
+ </td>
34
+ </tr>
35
+ </table>
@@ -0,0 +1,40 @@
1
+ <table>
2
+ <tr class="cardItem oddItem">
3
+ <td class="leftCol">
4
+ <div class="clear"></div>
5
+ <a href="../Card/Details.aspx?multiverseid=253682" id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardImageLink" onclick="return CardLinkAction(event, this, 'SameWindow');">
6
+ <img src="../../Handlers/Image.ashx?multiverseid=253682&amp;type=card" id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardImage" width="95" height="132" alt="Steam Vents" border="0" />
7
+ </a>
8
+ <div class="clear"></div>
9
+ </td>
10
+ <td class="middleCol">
11
+ <div class="clear"></div>
12
+ <div class="cardInfo">
13
+ <span class="cardTitle">
14
+ <a id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardTitle" onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=253682">Steam Vents</a></span> <span class="manaCost">
15
+ </span> (<span class="convertedManaCost">0</span>)
16
+ <br />
17
+ <span class="typeLine">
18
+ Land — Island Mountain
19
+ </span>
20
+ <div class="rulesText">
21
+ <p><i>(<img src="/Handlers/Image.ashx?size=small&amp;name=tap&amp;type=symbol" alt="Tap" align="absbottom" />: Add <img src="/Handlers/Image.ashx?size=small&amp;name=U&amp;type=symbol" alt="Blue" align="absbottom" /> or <img src="/Handlers/Image.ashx?size=small&amp;name=R&amp;type=symbol" alt="Red" align="absbottom" /> to your mana pool.)</i></p><p>As Steam Vents enters the battlefield, you may pay 2 life. If you don't, Steam Vents enters the battlefield tapped.</p></div>
22
+ </div>
23
+ </td>
24
+ <td class="rightCol setVersions">
25
+ <div class="clear"></div>
26
+ <div>
27
+ <div id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardSetCurrent" class="rightCol">
28
+ <a onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=253682"><img title="Return to Ravnica (Rare)" src="../../Handlers/Image.ashx?type=symbol&amp;set=RTR&amp;size=small&amp;rarity=R" alt="Return to Ravnica (Rare)" style="border-width:0px;" /></a>
29
+ </div>
30
+ </div>
31
+ <div id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_otherSetSection" class="otherSetSection">
32
+ <div>
33
+ <i>Other Versions</i></div>
34
+ <div id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardSets" class="rightCol">
35
+ <a onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=96923"><img title="Guildpact (Rare)" src="../../Handlers/Image.ashx?type=symbol&amp;set=GPT&amp;size=small&amp;rarity=R" alt="Guildpact (Rare)" style="border-width:0px;" /></a>
36
+ </div>
37
+ </div>
38
+ </td>
39
+ </tr>
40
+ </table>