forked from gfax/rbot-junkyard
-
Notifications
You must be signed in to change notification settings - Fork 0
/
brawl.rb
2487 lines (2406 loc) · 80.5 KB
/
brawl.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# coding: UTF-8
#
# :title: Junkyard
#
# Author:: Lite <[email protected]>
# Copyright:: (C) 2012 gfax.ch
# License:: GPL
# Version:: 2013-04-28
#
class Junkyard
TITLE = Bold + "Junkyard!" + Bold
MAX_HP = 10
COLORS = { :attack => Bold + Irc.color(:lightgray,:black),
:counter => Bold + Irc.color(:green,:black),
:disaster => Bold + Irc.color(:brown,:black),
:support => Bold + Irc.color(:teal,:black),
:unstoppable => Bold + Irc.color(:yellow,:black),
:skips => Irc.color(:purple),
:- => Irc.color(:red),
:+ => Irc.color(:blue)
}
# Custom death messages:
DEATHS = [ "Better luck next time, %{p}.",
"R.I.P. %{p}. Too soon.",
"%{p} died.",
"%{p} fell victim to HURT disease.",
"%{p} expired.",
"%{p} has been summoned by the Eternal Judge.",
"%{p} has gone upstream with the salmon.",
"%{p} is broken beyond repair. Nice work, ANDY.",
"%{p} left the stage.",
"%{p}: Loser even unto death.",
"%{p} paid the ultimate price.",
"%{p} perished.",
"%{p} shuffles off this mortal coil.",
"%{p} was discharged from mortality.",
"%{p} has foregone the finer things in life, like winning.",
"%{p}'s ded.",
"%{p}'s soul has passed on.",
"Fatal racing crash. Car flipped about 2,457 times. " +
"No survivors. %{p} was killed on impact. R.I.P."
]
BOT_DEATHS = [ "has seen better days",
"lived a better life than all you fools.",
"gives his last regards to Chanserv.",
"goes to meet that ol' pi in the sky.",
"wills his pancake collection to %{r}.",
"is invincible!",
"died the most honorable death.",
"can't feel his legs, but only because he has none."
]
# Things to say to non-players.
RETORTS = [ "Don't make me take you behind the tool shed, %{p}.",
"You don't have any cards, %{p}. I wonder why that is.",
"Sorry, %{p}, this is between me and the guys.",
"What do you need, %{p}?"
]
# Add to or modify these as you wish.
CARDS = {
:block => {
:type => :counter,
:string => "%{p} blocks %{o}'s %{c}.",
:regex => 'block',
:help => "Block a basic attack card when played against you. Can be " +
"used against a grab to nullify the grab's proceeding attack."
},
:dodge => {
:type => :counter,
:string => "%{p} dodges %{o}'s %{c}.",
:regex => [ /dodg/ ],
:help => "Similar to a block, but the attack is passed" +
"onto the next player. Cannot counter a grab."
},
:grab => {
:type => :counter,
:string => "%{p} grabs %{o}. Respond or pass, %{o}.",
:regex => 'grab',
:help => "Play this as a counter so you can attack back. This " +
"cannot be dodged. Also note this can be played before " +
"an attack to disguise your type of attack."
},
:mattress => {
:type => :counter,
:health => 2,
:string => "%{p} holds up an old mattress in defense.",
:regex => [ /mattres/ ],
:help => "Reduces opponent's attack by 2 points."
},
:insurance => {
:type => :counter,
:health => 5,
:string => "%{p} uses health insurance and " +
"is restored to 5 health!",
:regex => [ /insurance/ ],
:help => "Can only be used against a blockable, " +
"killing blow. Resets you to 5 health points."
},
:wrench => {
:type => :attack,
:string => "%{p} throws a wrench in %{o}'s gears.",
:skips => 2,
:regex => [ /wrench/ ],
:help => "Throw a wrench in your opponents' machinery. " +
"He must spend 2 turns finding what jammed his gears."
},
:nose_bleed => {
:type => :attack,
:health => -2,
:string => "%{p} pops %{o} in the nose, spraying blood everywhere.",
:skips => 1,
:regex => [ /nose/, /bleed/ ],
:help => "Opponent loses a turn to clean it up."
},
:gutpunch => {
:type => :attack,
:health => -2,
:string => "%{p} punches %{o} in the guts.",
:regex => [ /gut/, 'punch' ],
:help => "Basic attack."
},
:neck_punch => {
:type => :attack,
:health => -3,
:string => "%{p} delivers %{o} a punch in the neck.",
:regex => [ /neck/ ],
:help => "Slightly more powerful attack " +
"directed at the neck of your opponent."
},
:battery_acid => {
:type => :attack,
:health => -3,
:string => "%{p} throws battery acid in %{o}'s eyes.",
:skips => 1,
:regex => [ /battery/, /acid/ ],
:help => "Opponent is burned by battery acid and blinded for a turn."
},
:kickball => {
:type => :attack,
:health => -4,
:string => "%{p} delivers %{o}'s private belongings a swift kick.",
:regex => [ /kick/ ],
:help => "Major damage due to a swift kick in the balls. " +
"Can be used on players that don't have balls."
},
:uppercut => {
:type => :attack,
:health => -5,
:string => "%{o} receives an uppercut from %{p}.",
:regex => [ /upper/ ],
:help => "Ultimate attack."
},
:slot_machine => {
:type => :attack,
:string => "Next time stick to the pachinkos, %{o}.",
:regex => [ /slot/, /machine/ ],
:help => "Spits out three random attack values from 0 " +
"to 3. Attack does the sum of the three numbers."
},
:bulldozer => {
:type => :unstoppable,
:string => "%{p} bulldozes all the cards out of %{o}'s hand.",
:regex => [ /bull/, /dozer/ ],
:help => "Push all of your opponent's hand cards into " +
"the discard, leaving him vulnerable to attack."
},
:crane => {
:type => :unstoppable,
:string => "%{p}'s crane dumps some garbage cards on %{o}.",
:regex => [ /crane/ ],
:help => "Pick up all your cards you don't want and dump them on an " +
"opponent. The opponent won't get any new cards until " +
"he manages to get his hand below 5 cards again."
},
:tire => {
:type => :unstoppable,
:string => "%{p} throws a tire around %{o}.",
:skips => 1,
:regex => [ /tire(d|s)?\b/ ],
:help => "Throw a tire around your opponent, impeding " +
"his movement and causing him to lose a turn."
},
:trout_slap => {
:type => :unstoppable,
:health => -1,
:string => "%{p} slaps %{o} around a bit with a large trout.",
:regex => [ /trout/, /slap/ ],
:help => "An mIRC-inspired attack. Slap your opponent with a trout."
},
:a_gun => {
:type => :unstoppable,
:health => -2,
:string => "%{p} shoots %{o} in the FACE.",
:regex => [ /(a ?)?gun/ ],
:help => "Can't dodge a gun. Simple as that."
},
:tire_iron => {
:type => :unstoppable,
:health => -3,
:string => "%{p} whacks %{o} upside the head with a tire iron.",
:regex => [ /tire( |-)?iron/, 'iron' ],
:help => "Beat your defenseless opponent senseless."
},
:meal_steal => {
:type => :unstoppable,
:string => "%{p} rummages through %{o}'s lunchbox for soup and subs.",
:regex => [ /meal/, /steal/ ],
:help => "Steal all of an opponent's soup and subs, " +
"if he has any, and use them on yourself."
},
:soup => {
:type => :support,
:health => 1,
:string => "%{p} sips on some soup and relaxes.",
:regex => [ /soup/ ],
:help => "Take a sip. Relax. Gain up to #{MAX_HP} health."
},
:sub => {
:type => :support,
:health => 2,
:string => "%{p} eats a sub.",
:regex => 'sub',
:help => "Heal yourself by 2 points, " +
"up to a maximum of #{MAX_HP}."
},
:armor => {
:type => :support,
:health => 5,
:string => "%{p} buckles on some armor.",
:regex => 'armor',
:help => "Adds 5 extra points to your health on top of your maximum. " +
"Your main HP will be protected until the armor is depleted."
},
:surgery => {
:type => :support,
:health => MAX_HP - 1,
:string => "%{p} undergoes surgery and is completely restored!",
:regex => [ /s(e|u)rg(e|u)r/ ],
:help => "Used only when you have 1 health. " +
"Resets health to #{MAX_HP}."
},
:avalanche => {
:type => :disaster,
:health => -6,
:string => "%{p} causes an avalanche to fall on %{o}!",
:regex => [ /avalanch/ ],
:help => "A scrap pile avalanches! 6 damage to " +
"any random player, including yourself!."
},
:deflector => {
:type => :disaster,
:string => "%{p} raises a deflector shield!",
:regex => [ /deflect/ ],
:help => "Next attack played against you automatically " +
"attacks a random player that isn't you."
},
:earthquake => {
:type => :disaster,
:health => -1,
:string => "%{p} shakes everybody up with an earthquake!",
:regex => [ /earth/, /quake/ ],
:help => "An earthquake shakes the entire #{TITLE} " +
"1 damage to everyone, starting with yourself."
},
:reverse => {
:type => :disaster,
:string => "%{p} reverses the table!",
:regex => [ /reverse/ ],
:help => "REVERSE playing order. Skip " +
"opponent's turn if a 2-player game."
},
:shifty_business => {
:type => :disaster,
:string => "%{p} swaps hands with %{o}!",
:regex => [ /shift/, /business/ ],
:help => "Swap hand cards with a random player."
},
:spare_bolts => {
:type => :disaster,
:string => "%{p} fixes up an extra turn.",
:regex => [ /spare/, /bolt/ ],
:help => "Take an extra turn after your turn."
},
:the_bees => {
:name => 'THE BEES',
:type => :disaster,
:string => "%{p} drops the bee cage on %{o}'s head...",
:regex => [ /be+s/ ],
:help => "Random player is stung by bees and must do " +
"their best Nicholas Cage impression. 1 damage " +
"every turn until victim uses a support card."
},
:toolbox => {
:type => :disaster,
:string => "%{p} pulls %{n} cards from the deck.",
:regex => [ /tool/, /box/, /bag/ ],
:help => "Draw until you have 8 cards in your hand."
},
:windy => {
:name => 'It\'s Getting Windy',
:type => :disaster,
:string => "%{p} turns up the ceiling fan too high and blows up " +
"a gust! Every player passes a random card forward.",
:regex => [ /it\'?s\b/, /getting/, /windy/ ],
:help => "All players choose a random card " +
"from the player previous to them."
},
:whirlwind => {
:type => :disaster,
:string => "A whirlwind causes everyone to rotate hand cards!",
:regex => [ /whirl/, /wind/ ],
:help => "Every player shifts their hand cards " +
"over to the player in front of them."
}
}
GCARDS = {
:block => {
:type => :counter,
:string => "%{p} blocks %{o}'s %{c}.",
:regex => 'block',
:help => "Block a basic attack card when played against you. Can be " +
"used against a grab to nullify the grab's proceeding attack."
},
:dodge => {
:type => :counter,
:string => "%{p} dodges %{o}'s %{c}.",
:regex => [ /dodg/ ],
:help => "Similar to a block, but the attack is passed" +
"onto the next player. Cannot counter a grab."
},
:grab => {
:type => :counter,
:string => "%{p} grabs %{o}. Respond or pass, %{o}.",
:regex => 'grab',
:help => "Play this as a counter so you can attack back. This " +
"cannot be dodged. Also note this can be played before " +
"an attack to disguise your type of attack."
},
:mattress => {
:name => 'Wet Pillow',
:type => :counter,
:health => 2,
:string => "The wet pillow descends from heaven to shield %{p}.",
:regex => [ /wet/, /pillow/ ],
:help => "Reduces opponent's attack by 2 points."
},
:insurance => {
:name => 'Credit Feed',
:type => :counter,
:health => 5,
:string => "%{p} feeds me some credits " +
"and is restored to 5 health!",
:regex => [ /credit/, /feed/ ],
:help => "Can only be used against a blockable, " +
"killing blow. Resets you to 5 health points."
},
:wrench => {
:name => 'Roach Approach',
:type => :attack,
:string => "%{p} psychologically devastates " +
"%{o} with a Roach Approach.",
:skips => 2,
:regex => [ /roach/, /approach/ ],
:help => "This does no damage, but your opponent " +
"must spend 2 turns in therapy."
},
:nose_bleed => {
:name => 'Broken Heart',
:type => :attack,
:health => -2,
:string => "%{o} watches Jurassic Bark. :'(",
:skips => 1,
:regex => [ /broken/, /jur+as+ic/, 'heart', 'bark' ],
:help => "Opponent must watch Jurassic Bark, lose 2 health, and a turn."
},
:gutpunch => {
:type => :attack,
:health => -2,
:string => "A wild TODD appears and gutpunches %{o}.",
:regex => [ /gut/, 'punch' ],
:help => "Basic TODD-inspired technique."
},
:neck_punch => {
:type => :attack,
:health => -3,
:string => "%{p} delivers %{o} a punch in the neck.",
:regex => [ /neck/ ],
:help => "Slightly more powerful attack " +
"directed at the neck of your opponent."
},
:battery_acid => {
:name => 'Chihiro',
:type => :attack,
:health => -3,
:string => "Chihiro claws %{o} in an excited rage. She pees a little.",
:skips => 1,
:regex => [ /chihir/ ],
:help => "Chihiro claws your opponent in an excited rage. She " +
"pees a little. Opponent loses a turn to clean it up."
},
:kickball => {
:type => :attack,
:health => -4,
:string => "%{p} delivers %{o}'s #{[ 'toolbox', 'junk',
'private belongings', 'particle accelerator',
'anodes', 'anchors', 'Jimmy Deans', 'coat rack',
'balls' ].sample} a swift kick.",
:regex => [ /kick/ ],
:help => "Major damage due to a swift kick in the balls. " +
"Can be used on players that don't have balls."
},
:uppercut => {
:name => 'Touch',
:type => :attack,
:health => -5,
:string => "%{o} receives a touch from LordKaT.",
:regex => 'touch',
:help => "Ultimate attack."
},
:slot_machine => {
:type => :attack,
:string => "Next time stick to the pachinkos, %{o}.",
:regex => [ /slot/, /machine/ ],
:help => "Spits out three random attack values from 0 " +
"to 3. Attack does the sum of the three numbers."
},
:bulldozer => {
:name => 'Flipper',
:type => :unstoppable,
:string => [ "%{p} flips over the table, knocking all " +
"the cards out of %{o}'s hand.",
"%{p} violently SLAPS the cards out " +
"of %{o}'s hand for no raisin." ].sample,
:regex => [ /flipper/ ],
:help => "Opponent drops all his cards and draws new ones."
},
:crane => {
:name => 'Garbage Man',
:type => :unstoppable,
:string => "%{p} dumps a bunch of garbage cards on %{o}.",
:regex => [ /garbage/, /\bman\b/ ],
:help => "Pick up all your cards you don't want and dump them on an " +
"opponent. The opponent won't get any new cards until " +
"he manages to get his hand below 5 cards again."
},
:tire => {
:name => 'Looke Out',
:type => :unstoppable,
:string => "%{o} catches word of Looke coming " +
"to town and decides to wait around.",
:skips => 1,
:regex => [ /look/ ],
:help => "Looke's in town! He Says he's going to visit. " +
"Opponent loses a turn to wait for him. (He never comes.)"
},
:trout_slap => {
:name => 'Danger Zone',
:type => :unstoppable,
:health => -1,
:string => "%{p} leaves Danger Zone on repeat and " +
"forever ruins %{o}'s last.fm profile.",
:regex => [ /danger/, 'zone' ],
:help => "Roach scrobbles Danger Zone 570 times on your opponent's " +
"computer and now their music libraries are SUPER."
},
:a_gun => {
:type => :unstoppable,
:health => -2,
:string => "%{p} shoots %{o} in the FACE.",
:regex => [ /(a ?)?gun/ ],
:help => "Can't dodge a gun. Simple as that."
},
:tire_iron => {
:name => 'Rocket Lawn Chair',
:type => :unstoppable,
:health => -3,
:string => "%{p} blows up %{o} with a Rocket Lawn Chair.",
:regex => [ /tire/, /iron/ ],
:help => "Still not as good as a shotgun."
},
:meal_steal => {
:name => 'Heal Steal',
:type => :unstoppable,
:string => "%{p} rummages through %{o}'s hand for DDP and peelz.",
:regex => [ /heal/, /steal/ ],
:help => "Steal all of an opponent's DDP and peelz, " +
"if he has any, and use them on yourself."
},
:soup => {
:name => 'DDP',
:type => :support,
:health => 1,
:string => "%{p} takes a sip of DDP, relaxes.",
:regex => 'ddp',
:help => "Take a sip. Relax. Gain up to #{MAX_HP} health."
},
:sub => {
:name => 'Peelz',
:type => :support,
:health => 2,
:string => "%{p} peel'd up!",
:regex => 'peelz',
:help => "Heal yourself by 2 points, " +
"up to a maximum of #{MAX_HP}."
},
:armor => {
:type => :support,
:health => 5,
:string => "%{p} buckles on some armor.",
:regex => 'armor',
:help => "Adds 5 extra points to your health on top of your maximum. " +
"Your main HP will be protected until the armor is depleted."
},
:surgery => {
:name => 'White Wedding',
:type => :support,
:health => MAX_HP - 1,
:string => "It's a nice day to... START AGAIN!!! HEALTH RESTORED!!!",
:regex => [ /white/, /wedding/ ],
:help => "Used only when you have 1 health. " +
"Resets health to #{MAX_HP}."
},
:avalanche => {
:name => 'FFFFFF',
:type => :disaster,
:health => -6,
:string => "%{p} randomly inflicts 6 damage on %{o}. What a dick!",
:regex => [ /avalanch/ ],
:help => "A scrap pile avalanches! 6 damage to " +
"any random player, including yourself!."
},
:deflector => {
:type => :disaster,
:string => "%{p} raises a deflector shield!",
:regex => [ /deflect/ ],
:help => "Next attack played against you automatically " +
"attacks a random player that isn't you."
},
:earthquake => {
:name => 'Fireball',
:type => :disaster,
:health => -1,
:string => "%{p} drops a fireball on everyone.",
:regex => [ /fire/ ],
:help => "1 damage to everyone, starting with yourself."
},
:reverse => {
:name => 'You\'re Your Grandfather',
:type => :disaster,
:string => "%{p} reverses the table!",
:regex => [ /you'?re?/, /grand/, 'reverse' ],
:help => "Time is moving backwards! REVERSE playing order. " +
"Skip opponent's turn if a 2-player game."
},
:shifty_business => {
:type => :disaster,
:string => "%{p} swaps hands with %{o}!",
:regex => [ /shift/, /business/ ],
:help => "Swap hand cards with a random player."
},
:spare_bolts => {
:type => :disaster,
:string => "%{p} fixes up an extra turn.",
:regex => [ /spare/, /bolt/ ],
:help => "Take an extra turn after your turn."
},
:the_bees => {
:name => 'THE BEES',
:type => :disaster,
:string => "%{p} drops the bee cage on %{o}'s head...",
:regex => [ /be+s/ ],
:help => "Random player is stung by bees and must do " +
"their best Nicholas Cage impression. 1 damage " +
"every turn until victim uses a support card."
},
:toolbox => {
:type => :disaster,
:string => "%{p} pulls %{n} cards from the deck.",
:regex => [ /tool/, 'box', 'bag' ],
:help => "Draw until you have 8 cards in your hand."
},
:windy => {
:name => 'It\'s Getting Windy',
:type => :disaster,
:string => "%{p} turns up the ceiling fan too high and blows up " +
"a gust! Every player passes a random card forward.",
:regex => [ /it\'?s(( ?getting)? ?windy)?/, 'getting', 'windy' ],
:help => "All players choose a random card " +
"from the player previous to them."
},
:whirlwind => {
:type => :disaster,
:string => "FEEL THE POWER OF THE WIND",
:regex => [ /whirl/, /wind/ ],
:help => "Every player shifts their hand cards " +
"over to the player in front of them."
}
}
class Card
attr_reader :id, :name, :health, :skips, :string, :type
def initialize(channel, id)
@id = id
cards = if channel.name == '#gfax' then GCARDS else CARDS end
if cards[id].nil?
raise 'Invalid card name.'
return
end
@type = cards[id][:type]
@name = cards[id][:name]
@name = id.to_s.split('_').each{|w| w.capitalize!}.join(' ') if @name.nil?
@health = cards[id][:health] || 0
@skips = cards[id][:skips] || 0
@string = cards[id][:string]
end
def to_s
color = COLORS[type]
hs = if health.zero? then ''
elsif health < 0 then COLORS[:-] + ' ' + health.to_s
else COLORS[:+] + ' +' + health.to_s
end
ss = if skips.zero?
''
elsif hs == ''
COLORS[:skips] + ' ' + skips.to_s
else
Irc.color(:white) + '/' +
COLORS[:skips] + skips.to_s
end
color + "#{name}#{hs}#{ss}" + NormalText
end
end
class Player
attr_accessor :user, :bees, :blocks, :bonuses, :cards, :crane,
:damage, :deflector, :deflectors, :discard,
:glutton, :go_again, :grabbed, :hand_max, :health,
:skips, :skip_count, :turns, :turn_wizard
def initialize(user, health=MAX_HP)
@user = user # p.user => unbolded, p.to_s => bolded
@bees = false # player is attacked by bees when true
@blocks = 0 # counter for "NOPE" bonus
@bonuses = 0 # counter for end-of-game bonuses
@cards = [] # hand cards
@crane = nil # array of cards played with Crane
@damage = 0 # total damage dished out
@deflector = false # deflects attacks when true
@deflectors = 0 # counter for "Deflector" bonus
@discard = nil # card the player just played
@glutton = 0 # counter for "Glutton" bonus
@grabbed = false # currently being grabbed
@hand_max = 5 # maximum number of cards to deal up to
@health = health # initial health
@go_again = false # gets to go again when true
@skips = 0 # skips player when > 0
@skip_count = 0 # counter for "Where's-the-fight?" bonus
@turns = 0 # turns spent playing this game
@turn_wizard = 0 # counter for "Turn Wizard" bonus
end
def delete_cards(request)
request = [request] unless request.is_a?(Array)
request.each do |r|
# Grab an updated copy of the cards
# array before starting each iteration.
c = cards.dup
n = 0
n += 1 until c[n].id == r.id
@cards.delete_at(n)
end
end
def sort_cards
# .to_s => sorts by color then name
@cards = cards.sort {|x,y| x.to_s <=> y.to_s }
end
def to_s
if deflector
d1 = Irc.color(:brown) + "<" + NormalText
d2 = Irc.color(:brown) + ">" + NormalText
else
d1 = ''
d2 = ''
end
d1 + Bold + @user.to_s + Bold + d2
end
end
attr_reader :attacked, :channel, :deck, :discard, :dropouts,
:manager, :players, :registry, :slots, :started, :title
def initialize(plugin, channel, first_player, title)
@channel = channel
@plugin = plugin
@bot = plugin.bot
@attacked = nil # player being attacked
@deck = [] # card stock
@discard = [] # used cards
@dropouts = [] # users that aren't allowed to rejoin
@manager = nil # player that started the game
@players = [] # players currently in game
@registry = @plugin.registry
@slots = [] # slot machine damage
@started = nil # time the game started
@title = title # name of the game
create_deck
add_player(first_player)
end
def say(msg, opts={})
@bot.say channel, msg, opts
end
def notify(player, msg, opts={})
unless player.user == @bot.nick
@bot.notice player.user, msg, opts
end
end
def create_deck
10.times do
@deck << Card.new(channel, :gutpunch)
@deck << Card.new(channel, :neck_punch)
end
8.times do
@deck << Card.new(channel, :grab)
end
7.times do
@deck << Card.new(channel, :kickball)
@deck << Card.new(channel, :sub)
end
6.times do
@deck << Card.new(channel, :dodge)
end
5.times do
@deck << Card.new(channel, :block)
@deck << Card.new(channel, :uppercut)
end
3.times do
@deck << Card.new(channel, :mattress)
@deck << Card.new(channel, :nose_bleed)
@deck << Card.new(channel, :soup)
end
2.times do
@deck << Card.new(channel, :a_gun)
@deck << Card.new(channel, :battery_acid)
@deck << Card.new(channel, :insurance)
@deck << Card.new(channel, :meal_steal)
@deck << Card.new(channel, :slot_machine)
@deck << Card.new(channel, :surgery)
@deck << Card.new(channel, :tire)
@deck << Card.new(channel, :trout_slap)
@deck << Card.new(channel, :wrench)
end
1.times do
@deck << Card.new(channel, :armor)
@deck << Card.new(channel, :avalanche)
@deck << Card.new(channel, :bulldozer)
@deck << Card.new(channel, :crane)
@deck << Card.new(channel, :deflector)
@deck << Card.new(channel, :earthquake)
@deck << Card.new(channel, :spare_bolts)
@deck << Card.new(channel, :reverse)
@deck << Card.new(channel, :shifty_business)
@deck << Card.new(channel, :the_bees)
@deck << Card.new(channel, :tire_iron)
@deck << Card.new(channel, :toolbox)
@deck << Card.new(channel, :whirlwind)
@deck << Card.new(channel, :windy)
end
@deck.shuffle!
end
def deal(player, n=1)
return if n < 1
if deck.length < n
n -= deck.length
cards = @deck.pop(deck.length)
@deck = @discard.dup
@discard = []
@bot.action channel, "shuffles the deck."
@deck.shuffle!
cards |= @deck.pop(n)
else
cards = @deck.pop(n)
end
unless started.nil?
notify player, "#{Bold}You drew:#{Bold} #{cards.join(', ')}"
end
player.cards |= cards
player.sort_cards
end
def start_game
@players.shuffle!
@started = Time.now
increment_turn
players.each { |p| notify p, p_cards(p) unless p = players.first }
end
def add_player(user)
if p = get_player(user)
if p.user == @bot.nick
say "I'm already in the game, moron."
else
say "You're already in the game, #{p}."
end
return
end
@dropouts.each do |dp|
if dp.user == user
if user == @bot.nick
say "I was dropped from the game, moron."
else
say "You were dropped from the game, #{dp}. You can't get back in."
end
return
end
end
if started and @bot.config['junkyard.average_hp']
n = 0
players.each { |e| n += e.health }
hp = (n / players.length + 0.5).to_i
else
hp = MAX_HP
end
p = Player.new(user, hp)
@players << p
if manager.nil?
@manager = p
say "#{p} starts a #{title} Type 'j' to join."
else
say "#{p} joins the #{title}"
end
deal(p, p.hand_max)
if players.length == 2
countdown = @bot.config['junkyard.countdown']
@bot.timer.add_once(countdown) { start_game }
say "Game will start in #{Bold}#{countdown}#{Bold} seconds."
end
end
def drop_player(player, dropper=false)
unless dropper == false or dropper == manager or dropper == player
say "Only the game manager is allowed to drop others, #{dropper}."
return
end
# Pass any attacks on before removing a dropped player.
n = 0
n += 1 until players[n] == player
n = next_turn(n)
if player == manager and players.length > 2
unless players[n].user == @bot.nick
@manager = players[n]
else
@manager = players[next_turn(n)]
end
say "#{manager} is now game manager."
end
if dropper
# If the manager drops the only other player, end the game.
if dropper == manager and dropper != player and players.length < 3
say "#{player} has been removed from the game. #{title} stopped."
@plugin.remove_game(channel)
return
end
say "#{player} has been removed from the game."
# Pass any existing attacks to the next player (as
# long as the next player isn't the one attacking).
unless player == players.first
if players[n] == players.first
@attacked = players[next_turn(n)] if player == attacked
players[next_turn(n)].grabbed = true if player.grabbed
else
@attacked = players[n] if player == attacked
players[n].grabbed = true if player.grabbed
end
end
else
player.damage = 0
update_user_stats(player, 0)
if player.user == @bot.nick
r = if players.first.user == @bot.nick
players.last.user
else
players.first.user
end
@bot.action channel, BOT_DEATHS.sample % { :r => r }
else
say DEATHS.sample % { :p => player }
end
end
if @bot.config['junkyard.reveal_cards']
reveal_string = if player.cards.length > 0
"#{player} had #{player.cards.join(', ')}"
else
"#{player} had no cards"
end
reveal_string << " and an active #{player.deflector}" if player.deflector
reveal_string << " while suffering from #{player.bees}" if player.bees
reveal_string << "."
say reveal_string
end
player.discard = nil
player.grabbed = false
@discard |= player.cards
@discard |= player.crane if player.crane
@discard << player.bees if player.bees
@discard << player.deflector if player.deflector
@dropouts << player
@players.delete(player)
if players.length < 2
end_game
return
elsif not dropper
if attacked
attacked.discard = nil
attacked.grabbed = false
@attacked = nil
end
else
say p_turn
bot_thread_counter
bot_thread_move
end
end
def get_player(user, source=nil)
case user
when User
players.each do |p|
return p if p.user == user
end
when String
players.each do |p|
return p if p.user.irc_downcase == user.irc_downcase(channel.casemap)
end
players.each do |p|
if p.user.irc_downcase =~ /^#{user.irc_downcase(channel.casemap)}/
return p unless p.user.irc_downcase == source
end
end
else
get_player(user.to_s)
end
return nil
end
def current_discard
return title + " hasn't started yet." unless started
d_string = "Current discard is %{c}"
g_string = "%{o} has been grabbed by %{p}. " +
"Current discard is #{Bold}face down#{Bold}."
if attacked
if attacked.grabbed
return g_string % { :o => attacked.user, :p => players.first }
elsif players.first.grabbed
return g_string % { :o => players.first.user, :p => attacked }
elsif players.first.discard
return d_string % { :c => players.first.discard.to_s }
else
return d_string % { :c => attacked.discard.to_s }
end
end
return "#{players.first} hasn't attacked yet."
end
def bee_recover(player)
if player.bees
say "#{player} recovers from bee allergies."
@discard << player.bees
player.bees = false
end
end
def has_turn?(src)
return false unless started
return true if src == players.first.user
return false
end
def valid_insurance?(player, opponent)
bees = if player.bees then -1 else 0 end
damage = if opponent.discard then opponent.discard.health else 0 end
ensuing_health = player.health + damage + bees
if opponent.discard and opponent.discard.id == :slot_machine
slots.each { |n| ensuing_health -= n }
end
return true if ensuing_health < 1 and not player.deflector