Merge lp:~stefanor/ibid/mexican-shootout into lp:~ibid-core/ibid/old-trunk-pack-0.92

Proposed by Stefano Rivera
Status: Merged
Approved by: Stefano Rivera
Approved revision: 677
Merged at revision: 675
Proposed branch: lp:~stefanor/ibid/mexican-shootout
Merge into: lp:~ibid-core/ibid/old-trunk-pack-0.92
Diff against target: None lines
To merge this branch: bzr merge lp:~stefanor/ibid/mexican-shootout
Reviewer Review Type Date Requested Status
Jonathan Hitchcock Approve
Michael Gorven Approve
Review via email: mp+8095@code.launchpad.net

This proposal supersedes a proposal from 2009-06-27.

To post a comment you must log in.
Revision history for this message
Stefano Rivera (stefanor) wrote : Posted in a previous version of this proposal

Do we want this? The #compsci guys use it when drunk.

Revision history for this message
Jonathan Hitchcock (vhata) wrote : Posted in a previous version of this proposal

<3

review: Approve
lp:~stefanor/ibid/mexican-shootout updated
673. By Stefano Rivera

More acceptance commands

674. By Stefano Rivera

The agressor can't confirm

675. By Stefano Rivera

Spelling

676. By Stefano Rivera

Capitalise names on start

677. By Stefano Rivera

3 spelling errors

Revision history for this message
Michael Gorven (mgorven) wrote :

 review approve

review: Approve
Revision history for this message
Jonathan Hitchcock (vhata) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'ibid/plugins/core.py'
--- ibid/plugins/core.py 2009-05-08 16:21:03 +0000
+++ ibid/plugins/core.py 2009-07-01 16:59:59 +0000
@@ -85,6 +85,7 @@
85class Address(Processor):85class Address(Processor):
8686
87 processed = True87 processed = True
88 addressed = False
88 acknowledgements = Option('acknowledgements', 'Responses for positive acknowledgements',89 acknowledgements = Option('acknowledgements', 'Responses for positive acknowledgements',
89 (u'Okay', u'Sure', u'Done', u'Righto', u'Alrighty', u'Yessir'))90 (u'Okay', u'Sure', u'Done', u'Righto', u'Alrighty', u'Yessir'))
90 refusals = Option('refusals', 'Responses for negative acknowledgements',91 refusals = Option('refusals', 'Responses for negative acknowledgements',
9192
=== added file 'ibid/plugins/games.py'
--- ibid/plugins/games.py 1970-01-01 00:00:00 +0000
+++ ibid/plugins/games.py 2009-07-01 17:54:10 +0000
@@ -0,0 +1,392 @@
1import datetime
2import logging
3from random import choice, gauss, random
4import re
5import time
6
7import ibid
8from ibid.plugins import Processor, match, handler
9from ibid.config import Option, IntOption, BoolOption, FloatOption
10from ibid.utils import ibid_version
11
12help = {}
13log = logging.getLogger('plugins.games')
14
15help['duel'] = u"Duel at dawn, between channel members"
16class Duel(Processor):
17 u"""I challenge <user> to a duel [over <something>]
18 I demand satisfaction from <user> [over <something>]
19 I throw the gauntlet down at <user>'s feet [over <something>]
20 draw [my <weapon>]
21 bam|pew|bang|kapow|pewpew|holyhandgrenadeofantioch"""
22 feature = 'duel'
23
24 addressed = BoolOption('addressed', 'Must the bot be addressed', True)
25
26 extremities = Option('extremities', u'Extremities that can be hit', (
27 u'toe', u'foot', u'leg', u'thigh', u'finger', u'hand', u'arm',
28 u'elbow', u'shoulder', u'ear', u'nose', u'stomach',
29 ))
30
31 vitals = Option('vitals', 'Vital parts of the body that can be hit', (
32 u'head', u'groin', u'chest', u'heart', u'neck',
33 ))
34
35 happy_endings = Option('happy_endings', 'Both survive', (
36 u'walk off into the sunset', u'go for a beer', u'call it quits',
37 ))
38
39 weapons = Option('weapons', 'Weapons that can be used: name: (chance, damage)', {
40 u'bam': (0.75, 50),
41 u'pew': (0.75, 50),
42 u'fire': (0.75, 70),
43 u'bang': (0.75, 70),
44 u'kapow': (0.75, 90),
45 u'pewpew': (0.75, 110),
46 u'holyhandgrenadeofantioch': (1.0, 200),
47 })
48
49 draw_required = BoolOption('draw_required', 'Must you draw your weapon before firing?', True)
50 accept_timeout = FloatOption('accept_timeout', 'How long do we wait for acceptance?', 60.0)
51 start_delay = IntOption('start_delay', 'Time between acceptance and start of duel (rounded down to the highest minute)', 30)
52 timeout = FloatOption('timeout', 'How long is a duel on for', 10.0)
53 extratime = FloatOption('extratime', 'How much more time to grant after every shot fired?', 1.0)
54
55 duels = {}
56
57 class Duel(object):
58 pass
59
60 @match(r'^(?:I\s+)throw\s+(?:down\s+(?:the|my)\s+gauntlet|(?:the|my)\s+gauntlet\s+down)\s+'
61 r'at\s+(\S+?)(?:\'s\s+feet)?(?:\s+(?:over|because|for)\s+.+)?$')
62 def initiate_gauntlet(self, event, recipient):
63 self.initiate(event, recipient)
64
65 @match(r'^(?:I\s+)?demand\s+satisfaction\s+from\s+(\S+)(?:\s+(?:over|because|for)\s+.+)?$')
66 def initiate_satisfaction(self, event, recipient):
67 self.initiate(event, recipient)
68
69 @match(r'^(?:I\s+)?challenge\s+(\S+)(?:\s+to\s+a\s+duel)?(?:\s+(?:over|because|for)\s+.+)?$')
70 def initiate(self, event, recipient):
71 if not event.addressed:
72 return
73
74 if not event.public:
75 event.addresponse(choice((
76 u"All duels must take place in public places, by decree of the bot",
77 u"How do you expect to fight %(recipient)s, when he is not present?",
78 u"Your challenge must be made in public, Sir Knight",
79 )), {
80 'recipient': recipient
81 })
82 return
83
84 if (event.source, event.channel) in self.duels:
85 event.addresponse(choice((
86 u"We already have a war in here. Take your fight outside",
87 u"Isn't one fight enough? You may wait your turn",
88 )))
89 return
90
91 aggressor = event.sender['nick']
92
93 if recipient.lower() == aggressor.lower():
94 # Yes I know schizophrenia isn't the same as DID, but this sounds better :P
95 event.addresponse(choice((
96 u"Are you schizophrenic?",
97 u"Um, How exactly do you plan on fighting yourself?",
98 )))
99 return
100
101 if recipient.lower() in [name.lower() for name in ibid.config.plugins['core']['names']]:
102 event.addresponse(choice((
103 u"I'm a peaceful bot",
104 u"The ref can't take part in the battle",
105 u"You just want me to die. No way",
106 )))
107 return
108
109 duel = self.Duel()
110 self.duels[(event.source, event.channel)] = duel
111
112 duel.hp = {
113 aggressor.lower(): 100.0,
114 recipient.lower(): 100.0,
115 }
116 duel.names = {
117 aggressor.lower(): aggressor,
118 recipient.lower(): recipient,
119 }
120 duel.drawn = {
121 aggressor.lower(): False,
122 recipient.lower(): False,
123 }
124
125 duel.started = False
126 duel.confirmed = False
127 duel.aggressor = event.sender['nick'].lower()
128 duel.recipient = recipient.lower()
129
130 duel.cancel_callback = ibid.dispatcher.call_later(self.accept_timeout, self.cancel, event)
131
132 event.addresponse({'reply': (u'%(recipient)s: ' + choice((
133 u"The gauntlet has been thrown at your feet. Do you accept?",
134 u"You have been challenged. Do you accept?",
135 u"%(aggressor)s wishes to meet you at dawn on the field of honour. Do you accept?",
136 ))) % {
137 'recipient': recipient,
138 'aggressor': event.sender['nick'],
139 }})
140
141 def cancel(self, event):
142 duel = self.duels[(event.source, event.channel)]
143 del self.duels[(event.source, event.channel)]
144
145 event.addresponse(choice((
146 u"%(recipient)s appears to has fled the country during the night",
147 u"%(recipient)s refuses to meet your challenge and accepts dishonour",
148 u"Your challenge was not met. I suggest anger management councelling",
149 )), {
150 'recipient': duel.names[duel.recipient],
151 })
152
153 @match(r'^.*\b(?:ok|yes|I\s+do|sure|accept|hit\s+me)\b.*$')
154 def confirm(self, event):
155 if not event.addressed:
156 return
157
158 if (event.source, event.channel) not in self.duels:
159 return
160
161 duel = self.duels[(event.source, event.channel)]
162
163 if event.sender['nick'].lower() not in duel.names:
164 return
165
166 # Correct capitalisation
167 duel.names[event.sender['nick'].lower()] = event.sender['nick']
168
169 duel.confirmed = True
170 duel.cancel_callback.cancel()
171
172 now = datetime.datetime.now()
173 starttime = now + datetime.timedelta(seconds=self.start_delay + ((30 - now.second) % 30))
174 starttime = datetime.datetime(starttime.year, starttime.month, starttime.day,
175 starttime.hour, starttime.minute, starttime.second)
176 delay = starttime - now
177 delay = delay.seconds + (delay.microseconds / 10.**6)
178
179 duel.start_callback = ibid.dispatcher.call_later(delay, self.start, event)
180
181 event.addresponse({'reply': (
182 u"%(aggressor)s, %(recipient)s: "
183 u"The duel shall begin on the stroke of %(starttime)s %(timezone)s (in %(delay)s seconds). "
184 + choice((
185 u"You may clean your pistols.",
186 u"Prepare yourselves.",
187 u"Get ready",
188 ))
189 ) % {
190 'aggressor': duel.names[duel.aggressor],
191 'recipient': duel.names[duel.recipient],
192 'starttime': starttime.time().isoformat(),
193 'timezone': time.tzname[0],
194 'delay': (starttime - now).seconds,
195 }})
196
197 def start(self, event):
198 duel = self.duels[(event.source, event.channel)]
199
200 duel.started = True
201 duel.timeout_callback = ibid.dispatcher.call_later(self.timeout, self.end, event)
202
203 event.addresponse({'reply':
204 u"%s, %s: %s" % (duel.aggressor, duel.recipient, choice((
205 u'aaaand ... go!',
206 u'5 ... 4 ... 3 ... 2 ... 1 ... fire!',
207 u'match on!',
208 u'ready ... aim ... fire!'
209 )))})
210
211 def setup(self):
212 self.fire.im_func.pattern = re.compile(
213 r'^(%s)(?:[\s,.!:;].*)?$' % '|'.join(self.weapons.keys()),
214 re.I | re.DOTALL)
215
216 @match(r'^draw(?:s\s+h(?:is|er)\s+.*|\s+my\s+.*)?$')
217 def draw(self, event):
218 if (event.source, event.channel) not in self.duels:
219 if event.addressed:
220 event.addresponse(choice((
221 u"We do not permit drawn weapons here",
222 u"You may only draw a weapon on the field of honour",
223 )))
224 return
225
226 duel = self.duels[(event.source, event.channel)]
227
228 shooter = event.sender['nick']
229 if shooter.lower() not in duel.names:
230 event.addresponse(choice((
231 u"Spectators are not permitted to draw weapons",
232 u"Do you think you are %(fighter)s?",
233 )), {'fighter': choice(duel.names.values())})
234 return
235
236 if not duel.started:
237 event.addresponse(choice((
238 u"Now now, not so fast!",
239 u"Did I say go yet?",
240 u"Put that AWAY!",
241 )))
242 return
243
244 duel.drawn[shooter.lower()] = True
245 event.addresponse(True)
246
247 @handler
248 def fire(self, event, weapon):
249 shooter = event.sender['nick'].lower()
250 if (event.source, event.channel) not in self.duels:
251 return
252
253 duel = self.duels[(event.source, event.channel)]
254
255 if shooter not in duel.names:
256 event.addresponse(choice((
257 u"You aren't in a war",
258 u'You are a non-combatant',
259 u'You are a spectator',
260 )))
261 return
262
263 enemy = set(duel.names.keys())
264 enemy.remove(shooter)
265 enemy = enemy.pop()
266
267 if self.draw_required and not duel.drawn[shooter]:
268 recipient = shooter
269 else:
270 recipient = enemy
271
272 if not duel.started or not duel.confirmed:
273 if self.draw_required:
274 message = choice((
275 u"%(shooter)s tried to escape his duel by shooting himself in the foot. The duel has been cancelled, but his honour is forfiet",
276 u"%(shooter)s shot himself while preparing for his duel. The funeral will be held on the weekend",
277 ))
278 elif not duel.started:
279 message = choice((
280 u"FOUL! %(shooter)s fired before my mark. Just as well you didn't hit anything. I refuse to referee under these conditions",
281 u"FOUL! %(shooter)s injures %(enemy)s before the match started and is marched away in handcuffs",
282 u"FOUL! %(shooter)s killed %(enemy)s before the match started and was shot by the referee before he could hurt anyone else",
283 ))
284 else:
285 message = choice((
286 u"FOUL! The duel is not yet confirmed. %(shooter)s is marched away in handcuffs",
287 u"FOUL! Arrest %(shooter)s! Firing a weapon within city limits is not permitted",
288 ))
289 event.addresponse({'reply': message % {
290 'shooter': duel.names[shooter],
291 'enemy': duel.names[enemy],
292 }})
293 del self.duels[(event.source, event.channel)]
294 if duel.cancel_callback.active():
295 duel.cancel_callback.cancel()
296 if duel.start_callback.active():
297 duel.start_callback.cancel()
298 return
299
300 chance, power = self.weapons[weapon.lower()]
301
302 if random() < chance:
303 damage = max(gauss(power, power/2.0), 0)
304 duel.hp[recipient] -= damage
305 if duel.hp[recipient] <= 0.0:
306 del self.duels[(event.source, event.channel)]
307 duel.timeout_callback.cancel()
308 else:
309 duel.timeout_callback.delay(self.extratime)
310
311 params = {
312 'shooter': duel.names[shooter],
313 'enemy': duel.names[enemy],
314 'part': u'foot',
315 }
316 if shooter == recipient:
317 message = u"TRAGEDY: %(shooter)s shoots before drawing his weapon. "
318 if damage > 100.0:
319 message += choice((
320 u"The explosion killed him",
321 u"There was little left of him",
322 ))
323 elif duel.hp[recipient] <= 0.0:
324 message += choice((
325 u"Combined with his other injuries, he didn't stand a chance",
326 u"He died during field surgary",
327 ))
328 else:
329 message += choice((
330 u"Luckily, it was only a flesh wound",
331 u"He narrowly missed his femoral artery",
332 ))
333
334 elif damage > 100.0:
335 message = u'VICTORY: ' + choice((
336 u'%(shooter)s blows %(enemy)s away',
337 u'%(shooter)s destroys %(enemy)s',
338 ))
339 elif duel.hp[enemy] <= 0.0:
340 message = u'VICTORY: ' + choice((
341 u'%(shooter)s kills %(enemy)s with a shot to the %(part)s',
342 u'%(shooter)s shoots %(enemy)s killing him with a fatal shot to the %(part)s',
343 ))
344 params['part'] = choice(self.vitals)
345 else:
346 message = choice((
347 u'%(shooter)s hits %(enemy)s in the %(part)s, wounding him',
348 u'%(shooter)s shoots %(enemy)s in the %(part)s, but %(enemy)s can still fight',
349 ))
350 params['part'] = choice(self.extremities)
351
352 event.addresponse({'reply': message % params})
353
354 elif shooter == recipient:
355 event.addresponse({'reply': choice((
356 u"%s forget to draw his weapon. Luckily he missed his foot",
357 u"%s fires a holstered weapon. Luckily it only put a hole in his jacket",
358 u"%s won't win at this rate. He forgot to draw before firing. He missed himself too",
359 )) % duel.names[shooter]})
360 else:
361 event.addresponse({'reply': choice((
362 u'%s misses',
363 u'%s aims wide',
364 u'%s is useless with a weapon'
365 )) % duel.names[shooter]})
366
367 def end(self, event):
368 duel = self.duels[(event.source, event.channel)]
369 del self.duels[(event.source, event.channel)]
370
371 winner, loser = duel.names.keys()
372 if duel.hp[winner] < duel.hp[loser]:
373 winner, loser = loser, winner
374
375 if duel.hp[loser] == 100.0:
376 message = u"DRAW: %(winner)s and %(loser)s shake hands and %(ending)s"
377 elif duel.hp[winner] < 50.0:
378 message = u"DRAW: %(winner)s and %(loser)s bleed to death together"
379 elif duel.hp[loser] < 50.0:
380 message = u"VICTORY: %(loser)s bleeds to death"
381 elif duel.hp[winner] < 100.0:
382 message = u"DRAW: %(winner)s and %(loser)s hobble off together. Satisfaction is obtained"
383 else:
384 message = u"VICTORY: %(loser)s hobbles off while %(winner)s looks victorious"
385
386 event.addresponse({'reply': message % {
387 'loser': duel.names[loser],
388 'winner': duel.names[winner],
389 'ending': choice(self.happy_endings),
390 }})
391
392# vi: set et sta sw=4 ts=4:

Subscribers

People subscribed via source and target branches