Pokedex: colorify type efficacy.
[zzz-dywypi.git] / plugins / Pokedex / plugin.py
1 # encoding: utf8
2 ###
3 # Copyright (c) 2010, Alex Munroe
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are met:
8 #
9 # * Redistributions of source code must retain the above copyright notice,
10 # this list of conditions, and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above copyright notice,
12 # this list of conditions, and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
14 # * Neither the name of the author of this software nor the name of
15 # contributors to this software may be used to endorse or promote products
16 # derived from this software without specific prior written consent.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 # POSSIBILITY OF SUCH DAMAGE.
29
30 ###
31
32 import supybot.conf as conf
33 import supybot.utils as utils
34 from supybot.commands import *
35 import supybot.plugins as plugins
36 import supybot.ircutils as ircutils
37 import supybot.callbacks as callbacks
38
39 import pokedex.db
40 import pokedex.db.tables as tables
41 import pokedex.lookup
42
43 import urllib
44
45
46 def get_stat_color(stat):
47 if stat < 41:
48 return 4 # red
49 elif stat < 52:
50 return 7 # orange
51 elif stat < 61:
52 return 8 # yellow
53 elif stat < 71:
54 return 9 # green
55 elif stat < 85:
56 return 11 # cyan
57 elif stat < 100:
58 return 12 # blue
59 else:
60 return 13 # purple
61
62
63 class Pokedex(callbacks.Plugin):
64 """Add the help for "@plugin help Pokedex" here
65 This should describe *how* to use this plugin."""
66 def __init__(self, irc):
67 self.__parent = super(Pokedex, self)
68 self.__parent.__init__(irc)
69 self.db = pokedex.db.connect(self.registryValue('databaseURL'))
70 self.indices = pokedex.lookup.open_index(
71 directory=conf.supybot.directories.data.dirize('pokedex-index'),
72 session=self.db,
73 )
74
75 def pokedex(self, irc, msg, args, thing):
76 """<thing...>
77
78 Looks up <thing> in the veekun Pokédex."""
79
80 # Fix encoding. Sigh.
81 if not isinstance(thing, unicode):
82 ascii_thing = thing
83 try:
84 thing = ascii_thing.decode('utf8')
85 except UnicodeDecodeError:
86 thing = ascii_thing.decode('latin1')
87
88 # Similar logic to the site, here.
89 results = pokedex.lookup.lookup(thing, session=self.db,
90 indices=self.indices)
91
92 # Nothing found
93 if len(results) == 0:
94 self._reply(irc, "I don't know what that is.")
95 return
96
97 # Multiple matches; propose them all
98 if len(results) > 1:
99 if results[0].exact:
100 reply = "Are you looking for"
101 else:
102 reply = "Did you mean"
103
104 # For exact name matches with multiple results, use type prefixes
105 # (item:Metronome). For anything else, omit them
106 use_prefixes = (results[0].exact
107 and '*' not in thing
108 and '?' not in thing)
109
110 result_strings = []
111 for result in results:
112 result_string = result.object.name
113
114 # Prepend, e.g., pokemon: if necessary
115 if use_prefixes:
116 # Table classes know their singular names
117 prefix = result.object.__singlename__
118 result_string = prefix + ':' + result_string
119
120 # Identify foreign language names
121 if result.language:
122 result_string += u""" ({0}: {1})""".format(
123 result.iso3166, result.name)
124
125 result_strings.append(result_string)
126
127 self._reply(irc, u"{0}: {1}?".format(reply, '; '.join(result_strings)))
128 return
129
130 # If we got here, there's an exact match; hurrah!
131 result = results[0]
132 obj = result.object
133 if isinstance(obj, tables.Pokemon):
134 reply_template = \
135 u"""#{id} {name}, {type}-type Pokémon. Has {abilities}. """ \
136 """{stats}. """ \
137 """http://veekun.com/dex/pokemon/{link_name}"""
138
139 if obj.forme_name:
140 name = '{form} {name}'.format(
141 form=obj.forme_name.title(),
142 name=obj.name
143 )
144 else:
145 name = obj.name
146
147 if obj.forme_base_pokemon:
148 # Can't use urllib.quote() on the whole thing or it'll
149 # catch "?" and "=" where it shouldn't.
150 # XXX Also we need to pass urllib.quote() things explicitly
151 # encoded as utf8 or else we get a UnicodeEncodeError.
152 link_name = '{name}?form={form}'.format(
153 name=urllib.quote(obj.name.lower().encode('utf8')),
154 form=urllib.quote(obj.forme_name.lower().encode('utf8')),
155 )
156 else:
157 link_name = urllib.quote(obj.name.lower().encode('utf8'))
158
159 # a/b/c/d/e/f sucks. Put stats in a more readable format.
160 # Also, color-code them by good-osity.
161 colored_stats = []
162 stat_total = 0
163 for pokemon_stat in obj.stats:
164 base_stat = pokemon_stat.base_stat
165 stat_total += base_stat
166 color = get_stat_color(base_stat)
167
168 colored_stats.append(
169 "\x03{0:02d}{1}\x0f".format(color, base_stat)
170 )
171
172 colored_stat_total = "\x03{0:02d}{1}\x0f".format(
173 get_stat_color(stat_total / 6),
174 stat_total,
175 )
176 stats = """{0} HP, {1}/{2} phys, {3}/{4} spec, {5} speed, {total} total""" \
177 .format(*colored_stats, total=colored_stat_total)
178 self._reply(irc, reply_template.format(
179 id=obj.national_id,
180 name=name,
181 type='/'.join(_.name for _ in obj.types),
182 abilities=' or '.join(_.name for _ in obj.abilities),
183 stats=stats,
184 link_name=link_name,
185 )
186 )
187
188 elif isinstance(obj, tables.Move):
189 reply_template = \
190 u"""{name}, {type}-type {damage_class} move. """ \
191 """{power} power; {accuracy}% accuracy; {pp} PP. """ \
192 """{effect} """ \
193 """http://veekun.com/dex/moves/{link_name}"""
194 self._reply(irc, reply_template.format(
195 name=obj.name,
196 type=obj.type.name,
197 damage_class=obj.damage_class.name,
198 power=obj.power,
199 accuracy=obj.accuracy,
200 pp=obj.pp,
201 effect=unicode(obj.short_effect.as_text),
202 link_name=urllib.quote(obj.name.lower().encode('utf8')),
203 )
204 )
205
206 elif isinstance(obj, tables.Type):
207 reply_template = u"""{name}, a type. """
208
209 offensive_reply_factors = {
210 200: u'\x03092×\x0f',
211 50: u'\x0304½×\x0f',
212 0: u'\x03140×\x0f',
213 }
214
215 offensive_modifiers = {}
216 for matchup in obj.damage_efficacies:
217 if matchup.damage_factor != 100:
218 offensive_modifiers.setdefault(matchup.damage_factor, []) \
219 .append(matchup.target_type.name)
220 if offensive_modifiers:
221 reply_template += u"""{offensive_modifiers}. """
222 for factor in offensive_modifiers:
223 offensive_modifiers[factor] = u'{factor} against {types}'.format(
224 factor=offensive_reply_factors[factor],
225 types=', '.join(sorted(offensive_modifiers[factor]))
226 )
227
228 defensive_reply_factors = {
229 200: u'\x03042×\x0f',
230 50: u'\x0309½×\x0f',
231 0: u'\x03110×\x0f',
232 }
233
234 defensive_modifiers = {}
235 for matchup in obj.target_efficacies:
236 if matchup.damage_factor != 100:
237 defensive_modifiers.setdefault(matchup.damage_factor, []) \
238 .append(matchup.damage_type.name)
239 if defensive_modifiers:
240 reply_template += u"""{defensive_modifiers}. """
241 for factor in defensive_modifiers:
242 defensive_modifiers[factor] = u'{factor} from {types}'.format(
243 factor=defensive_reply_factors[factor],
244 types=', '.join(sorted(defensive_modifiers[factor]))
245 )
246
247 reply_template += u"""http://veekun.com/dex/types/{link_name}"""
248
249 self._reply(irc, reply_template.format(
250 name=obj.name.capitalize(),
251 offensive_modifiers='; '.join(offensive_modifiers[_]
252 for _ in sorted(offensive_modifiers)),
253 defensive_modifiers='; '.join(defensive_modifiers[_]
254 for _ in sorted(defensive_modifiers)),
255 link_name=urllib.quote(obj.name.lower().encode('utf8')),
256 )
257 )
258
259 elif isinstance(obj, tables.Item):
260 reply_template = \
261 u"""{name}, an item. """ \
262 """http://veekun.com/dex/items/{link_name}"""
263 self._reply(irc, reply_template.format(
264 name=obj.name,
265 link_name=urllib.quote(obj.name.lower().encode('utf8')),
266 )
267 )
268
269 elif isinstance(obj, tables.Ability):
270 reply_template = \
271 u"""{name}, an ability. {effect} """ \
272 """http://veekun.com/dex/abilities/{link_name}"""
273 self._reply(irc, reply_template.format(
274 name=obj.name,
275 effect=obj.effect,
276 link_name=urllib.quote(obj.name.lower().encode('utf8')),
277 )
278 )
279
280 else:
281 # This can only happen if lookup.py is upgraded and we are not
282 self._reply(irc, "Uhh.. I found that, but I don't know what it is. :(")
283
284 pokedex = wrap(pokedex, [rest('something')])
285
286 def _reply(self, irc, response):
287 """Wraps irc.reply() to do some Unicode decoding."""
288 if isinstance(response, str):
289 irc.reply(response)
290 else:
291 irc.reply(response.encode('utf8'))
292
293
294 Class = Pokedex
295
296
297 # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: