]>
Commit | Line | Data |
---|---|---|
1 | package Mafia; | |
2 | ||
3 | use 5.010001; | |
4 | use strict; | |
5 | use warnings; | |
6 | use parent qw/Exporter/; | |
7 | ||
8 | use constant; | |
9 | use Storable qw/dclone/; | |
10 | ||
11 | our $VERSION = '0.001002'; | |
12 | ||
13 | sub defconst { constant->import($_ => $_) for @_ } | |
14 | ||
15 | BEGIN { | |
16 | # Roles | |
17 | defconst qw/vanilla goon doctor vigilante roleblocker jailkeeper gunsmith tracker watcher bodyguard rolecop cop sk hider/; | |
18 | ||
19 | # Factions | |
20 | defconst qw/mafia town/; | |
21 | ||
22 | # Extra traits | |
23 | defconst qw/miller godfather weak macho bulletproof/; | |
24 | ||
25 | # Messages | |
26 | defconst qw/MSG_NIGHT MSG_DAY MSG_PLAYERS_ALIVE MSG_DEATH MSG_GUNCHECK MSG_NORESULT MSG_TRACK MSG_WATCH MSG_COP MSG_ROLECOP/; | |
27 | ||
28 | # Action types | |
29 | defconst qw/ACT_KILL ACT_LYNCH ACT_PROTECT ACT_GUARD ACT_ROLEBLOCK ACT_GUNCHECK ACT_TRACK ACT_WATCH ACT_ROLECOP ACT_COP ACT_TRACK_RESULT ACT_WATCH_RESULT ACT_HIDE/; | |
30 | } | |
31 | ||
32 | use constant +{ | |
33 | townie => town, | |
34 | ROLE => [vanilla, goon, doctor, vigilante, roleblocker, jailkeeper, gunsmith, tracker, watcher, bodyguard, rolecop, cop, sk, hider], | |
35 | FACTION => [mafia, town], | |
36 | FLAG => [miller, godfather, weak, macho, bulletproof], | |
37 | ACTION_ORDER => [ACT_HIDE, ACT_ROLEBLOCK, ACT_PROTECT, ACT_GUARD, ACT_GUNCHECK, ACT_ROLECOP, ACT_COP, ACT_TRACK, ACT_WATCH, ACT_KILL, ACT_LYNCH, ACT_TRACK_RESULT, ACT_WATCH_RESULT], | |
38 | INVESTIGATIVE_ACTIONS => [ACT_GUNCHECK, ACT_TRACK, ACT_WATCH, ACT_ROLECOP, ACT_COP], | |
39 | GUNROLES => [vigilante, gunsmith], | |
40 | }; | |
41 | ||
42 | my %ROLE_HASH = map { $_ => 1 } @{ROLE()}; | |
43 | my %FACTION_HASH = map { $_ => 1 } @{FACTION()}; | |
44 | my %FLAG_HASH = map { $_ => 1 } @{FLAG()}; | |
45 | my %INVESTIGATIVE_ACTIONS_HASH = map { $_ => 1 } @{INVESTIGATIVE_ACTIONS()}; | |
46 | my %GUNROLES_HASH = map { $_ => 1 } @{GUNROLES()}; | |
47 | ||
48 | our @EXPORT = do { | |
49 | no strict 'refs'; | |
50 | grep { $_ !~ [qw/import/] and exists &$_ } keys %{__PACKAGE__ . '::'}; | |
51 | }; | |
52 | ||
53 | ################################################## Helper subs | |
54 | ||
55 | sub import { | |
56 | strict->import; | |
57 | goto &Exporter::import; | |
58 | } | |
59 | ||
60 | my (%players, %tplayers, @actions); | |
61 | my $daycnt = 0; | |
62 | my $nightcnt = 0; | |
63 | my $isday = 0; | |
64 | my $first = 1; | |
65 | ||
66 | sub clean{ | |
67 | %players = (); | |
68 | %tplayers = (); | |
69 | @actions = (); | |
70 | $daycnt = 0; | |
71 | $nightcnt = 0; | |
72 | $isday = 0; | |
73 | $first = 1; | |
74 | } | |
75 | ||
76 | sub uniq { | |
77 | my %hash = map { $_ => 1 } @_; | |
78 | keys %hash | |
79 | } | |
80 | ||
81 | sub phase { | |
82 | return "Day $daycnt" if $isday; | |
83 | return "Night $nightcnt" unless $isday; | |
84 | } | |
85 | ||
86 | sub rolename { | |
87 | my %player = %{$players{$_[0]}}; | |
88 | my ($faction, $role) = ($player{faction}, $player{role}); | |
89 | if (defined $faction && $faction eq town && $role eq vanilla) { | |
90 | undef $faction; | |
91 | $role = 'Vanilla Townie'; | |
92 | } | |
93 | my @tokens = (); | |
94 | push @tokens, ucfirst $faction if $faction; | |
95 | for my $flag (@{FLAG()}) { | |
96 | push @tokens, ucfirst $flag if $player{$flag} | |
97 | } | |
98 | push @tokens, ucfirst $role unless $role eq goon && $player{godfather}; | |
99 | "@tokens" | |
100 | } | |
101 | ||
102 | sub msg { | |
103 | my ($type, @args) = @_; | |
104 | my %msg_lut = ( | |
105 | MSG_NIGHT => sub { | |
106 | my ($night) = @args; | |
107 | say '' unless $first; | |
108 | $first = 0; | |
109 | say "It is Night $night"; | |
110 | }, | |
111 | ||
112 | MSG_DAY => sub { | |
113 | my ($day) = @args; | |
114 | say '' unless $first; | |
115 | $first = 0; | |
116 | say "It is Day $day"; | |
117 | }, | |
118 | ||
119 | MSG_PLAYERS_ALIVE => sub { | |
120 | @args = sort @args; | |
121 | say "Players alive: ", join ', ', @args | |
122 | }, | |
123 | ||
124 | MSG_DEATH => sub { | |
125 | my %args = @args; | |
126 | my ($who, $reason) = @args{'target', 'reason'}; | |
127 | my $phase = phase; | |
128 | my $rolename = rolename $who; | |
129 | say "$who ($rolename) — $reason $phase"; | |
130 | }, | |
131 | ||
132 | MSG_GUNCHECK => sub { | |
133 | my %args = @args; | |
134 | my ($gunsmith, $who, $hasgun) = @args{'source', 'target', 'result'}; | |
135 | say "$gunsmith: $who has a gun" if $hasgun; | |
136 | say "$gunsmith: $who does not have a gun" unless $hasgun; | |
137 | }, | |
138 | ||
139 | MSG_NORESULT => sub { | |
140 | my %args = @args; | |
141 | my ($who) = $args{'source'}; | |
142 | say "$who: No result" | |
143 | }, | |
144 | ||
145 | MSG_TRACK => sub { | |
146 | my %args = @args; | |
147 | my ($tracker, $who, $result) = @args{'source', 'target', 'result'}; | |
148 | my @result = @{$result}; | |
149 | local $, = ', '; | |
150 | say "$tracker: $who did not visit anyone" unless scalar @result; | |
151 | say "$tracker: $who visited: @result" if scalar @result; | |
152 | }, | |
153 | ||
154 | MSG_WATCH => sub { | |
155 | my %args = @args; | |
156 | my ($watcher, $who, $result) = @args{'source', 'target', 'result'}; | |
157 | my @result = @{$result}; | |
158 | local $, = ', '; | |
159 | say "$watcher: $who was not visited by anyone" unless scalar @result; | |
160 | say "$watcher: $who was visited by: @result" if scalar @result; | |
161 | }, | |
162 | ||
163 | MSG_ROLECOP => sub { | |
164 | my %args = @args; | |
165 | my ($rolecop, $who, $role) = @args{'source', 'target', 'result'}; | |
166 | say "$rolecop: $who\'s role is: $role" | |
167 | }, | |
168 | ||
169 | MSG_COP => sub { | |
170 | my %args = @args; | |
171 | my ($cop, $who, $ismafia) = @args{'source', 'target', 'result'}; | |
172 | say "$cop: $who is mafia" if $ismafia; | |
173 | say "$cop: $who is not mafia" unless $ismafia; | |
174 | }, | |
175 | ); | |
176 | ||
177 | $msg_lut{$type}->(); | |
178 | } | |
179 | ||
180 | sub putaction { | |
181 | my ($delay, $type, %args) = @_; | |
182 | $actions[$delay]->{$type} //= []; | |
183 | if (exists $args{target} && exists $args{source} && $players{$args{target}}{faction} eq mafia && $players{$args{source}}{weak}) { | |
184 | putaction($delay, ACT_KILL, target => $args{source}, reason => 'targeted scum'); | |
185 | } | |
186 | push @{$actions[$delay]->{$type}}, \%args | |
187 | } | |
188 | ||
189 | sub doaction { | |
190 | my ($type, $args) = @_; | |
191 | my %args = %$args; | |
192 | my $source = $args{source}; | |
193 | my $target = $args{target}; | |
194 | if (defined $source && defined $target) { | |
195 | # Watcher and tracker variables | |
196 | $tplayers{$source}{targets} //= []; | |
197 | push @{$tplayers{$source}{targets}}, $target; | |
198 | $tplayers{$target}{sources} //= []; | |
199 | push @{$tplayers{$target}{sources}}, $source; | |
200 | ||
201 | # Copy this action to everybody hiding behind $target | |
202 | if (exists $tplayers{$target}{hiders}) { | |
203 | for my $target (@{$tplayers{$target}{hiders}}) { | |
204 | my %args = %args; | |
205 | $args{target} = $target; | |
206 | $args{hidepierce} = 1; | |
207 | doaction($type, \%args); | |
208 | } | |
209 | } | |
210 | ||
211 | # Check if the action should be blocked | |
212 | my $strongkill = $type eq ACT_KILL && $args{strong}; | |
213 | my $roleblocked = $tplayers{$source}{roleblocked}; | |
214 | my $hidden = $tplayers{$target}{hidden}; | |
215 | my $hidepierce = $args{hidepierce}; | |
216 | if ($source && (( $roleblocked && !$strongkill ) || ($hidden && !$hidepierce) )) { | |
217 | msg MSG_NORESULT, %args if $INVESTIGATIVE_ACTIONS_HASH{$type}; | |
218 | return | |
219 | } | |
220 | } | |
221 | ||
222 | my %act_lut = ( | |
223 | ACT_KILL => sub { | |
224 | break if $tplayers{$target}{bulletproof} && defined $source; | |
225 | if ($tplayers{$target}{guard_count} && defined $source) { | |
226 | $tplayers{$target}{guard_count}--; | |
227 | # Copy this action to the first guard | |
228 | $args{target} = shift @{$tplayers{$target}{guards}}; | |
229 | @_ = ($type, %args); | |
230 | goto &doaction; | |
231 | } | |
232 | if ($tplayers{$target}{protection} && !$args{strong}) { | |
233 | $tplayers{$target}{protection}--; | |
234 | break | |
235 | } | |
236 | msg MSG_DEATH, %args; | |
237 | delete $players{$target} | |
238 | }, | |
239 | ||
240 | ACT_LYNCH => sub { | |
241 | if ($tplayers{$target}{guard_count}) { | |
242 | $tplayers{$target}{guard_count}--; | |
243 | $args{target} = shift @{$tplayers{$target}{guards}}; | |
244 | $target=$args{target}; | |
245 | } | |
246 | if ($tplayers{$target}{protection}) { | |
247 | $tplayers{$target}{protection}--; | |
248 | break | |
249 | } | |
250 | msg MSG_DEATH, %args, reason => 'lynched'; | |
251 | delete $players{$target} | |
252 | }, | |
253 | ||
254 | ACT_PROTECT => sub { | |
255 | my $count = $args{count} // 1; | |
256 | $tplayers{$target}{protection} += $count unless $tplayers{$target}{macho} | |
257 | }, | |
258 | ||
259 | ACT_ROLEBLOCK => sub { | |
260 | $tplayers{$target}{roleblocked} = 1 | |
261 | }, | |
262 | ||
263 | ACT_GUNCHECK => sub { | |
264 | my $role = $players{$target}{role}; | |
265 | my $hasgun = $GUNROLES_HASH{$role} || ($players{$target}{faction} eq mafia && $role ne doctor); | |
266 | msg MSG_GUNCHECK, %args, result => $hasgun | |
267 | }, | |
268 | ||
269 | ACT_TRACK_RESULT => sub { | |
270 | msg MSG_TRACK, %args, result => [ uniq @{$tplayers{$target}{targets} // []} ]; | |
271 | }, | |
272 | ||
273 | ACT_WATCH_RESULT => sub { | |
274 | msg MSG_WATCH, %args, result => [ uniq @{$tplayers{$target}{sources} // []} ]; | |
275 | }, | |
276 | ||
277 | ACT_GUARD => sub { | |
278 | $tplayers{$target}{guard_count}++; | |
279 | $tplayers{$target}{guards} //= []; | |
280 | push @{$tplayers{$target}{guards}}, $source; | |
281 | }, | |
282 | ||
283 | ACT_ROLECOP => sub { | |
284 | my $result = $players{$target}{role}; | |
285 | $result = vanilla if $result eq goon; | |
286 | msg MSG_ROLECOP, %args, result => ucfirst $result | |
287 | }, | |
288 | ||
289 | ACT_COP => sub { | |
290 | my $result = $players{$target}{faction} eq mafia; | |
291 | $result = 1 if $players{$target}{miller}; | |
292 | $result = 0 if $players{$target}{godfather}; | |
293 | msg MSG_COP, %args, result => $result | |
294 | }, | |
295 | ||
296 | ACT_HIDE => sub { | |
297 | $tplayers{$source}{hidden} = 1; | |
298 | $tplayers{$target}{hiders} //= []; | |
299 | push @{$tplayers{$target}{hiders}}, $source | |
300 | }, | |
301 | ); | |
302 | ||
303 | $act_lut{$type}->(); | |
304 | } | |
305 | ||
306 | sub process_phase_change { | |
307 | %tplayers = %{dclone \%players}; | |
308 | my $actions = shift @actions; | |
309 | for my $type (@{ACTION_ORDER()}) { | |
310 | doaction $type, $_ for @{$actions->{$type}} | |
311 | } | |
312 | } | |
313 | ||
314 | ################################################## User subs | |
315 | ||
316 | sub player { | |
317 | my ($name, @args) = @_; | |
318 | my %player; | |
319 | for my $trait (@args) { | |
320 | $player{role} = $trait if $ROLE_HASH{$trait}; | |
321 | $player{faction} = $trait if $FACTION_HASH{$trait}; | |
322 | $player{$trait} = 1 if $FLAG_HASH{$trait}; | |
323 | } | |
324 | ||
325 | $players{$name} = \%player; | |
326 | } | |
327 | ||
328 | sub day { | |
329 | process_phase_change; | |
330 | $isday = 1; | |
331 | msg MSG_DAY, ++$daycnt; | |
332 | msg MSG_PLAYERS_ALIVE, keys %players; | |
333 | } | |
334 | ||
335 | sub night { | |
336 | process_phase_change; | |
337 | $isday = 0; | |
338 | msg MSG_NIGHT, ++$nightcnt; | |
339 | msg MSG_PLAYERS_ALIVE, keys %players; | |
340 | } | |
341 | ||
342 | sub lynch { | |
343 | my ($who) = @_; | |
344 | putaction 0, ACT_LYNCH, target => $who; | |
345 | } | |
346 | ||
347 | sub factionkill { | |
348 | my ($killer, $who, $reason, @args) = @_; | |
349 | putaction 0, ACT_KILL, target => $who, source => $killer, reason => $reason, @args; | |
350 | } | |
351 | ||
352 | sub protect { | |
353 | my ($doctor, $who) = @_; | |
354 | putaction 0, ACT_PROTECT, target => $who, source => $doctor; | |
355 | } | |
356 | ||
357 | sub vig { | |
358 | my ($vig, $who, $reason, @args) = @_; | |
359 | putaction 0, ACT_KILL, target => $who, source => $vig, reason => $reason, @args; | |
360 | } | |
361 | ||
362 | sub roleblock { | |
363 | my ($roleblocker, $who) = @_; | |
364 | putaction 0, ACT_ROLEBLOCK, target => $who, source => $roleblocker; | |
365 | } | |
366 | ||
367 | sub jailkeep { | |
368 | my ($jailkeeper, $who) = @_; | |
369 | putaction 0, ACT_ROLEBLOCK, target => $who, source => $jailkeeper; | |
370 | putaction 0, ACT_PROTECT, target => $who, source => $jailkeeper, count => 1000; | |
371 | } | |
372 | ||
373 | sub guncheck { | |
374 | my ($gunsmith, $who) = @_; | |
375 | putaction 0, ACT_GUNCHECK, target => $who, source => $gunsmith; | |
376 | } | |
377 | ||
378 | sub track { | |
379 | my ($tracker, $who) = @_; | |
380 | putaction 0, ACT_TRACK, target => $who, source => $tracker; | |
381 | putaction 0, ACT_TRACK_RESULT, target => $who, source => $tracker; | |
382 | } | |
383 | ||
384 | sub watch { | |
385 | my ($watcher, $who) = @_; | |
386 | putaction 0, ACT_WATCH, target => $who, source => $watcher; | |
387 | putaction 0, ACT_WATCH_RESULT, target => $who, source => $watcher; | |
388 | } | |
389 | ||
390 | sub guard { | |
391 | my ($guard, $who) = @_; | |
392 | putaction 0, ACT_GUARD, target => $who, source => $guard; | |
393 | } | |
394 | ||
395 | sub rolecopcheck { | |
396 | my ($rolecop, $who) = @_; | |
397 | putaction 0, ACT_ROLECOP, target => $who, source => $rolecop; | |
398 | } | |
399 | ||
400 | sub copcheck { | |
401 | my ($cop, $who) = @_; | |
402 | putaction 0, ACT_COP, target => $who, source => $cop; | |
403 | } | |
404 | ||
405 | sub skill { | |
406 | my ($sk, $who, $reason, @args) = @_; | |
407 | putaction 0, ACT_KILL, target => $who, source => $sk, reason => $reason, @args; | |
408 | } | |
409 | ||
410 | sub hide { | |
411 | my ($hider, $who) = @_; | |
412 | putaction 0, ACT_HIDE, target => $who, source => $hider; | |
413 | } | |
414 | ||
415 | 1; | |
416 | __END__ | |
417 | ||
418 | =encoding utf-8 | |
419 | ||
420 | =head1 NAME | |
421 | ||
422 | Mafia - easily moderate Mafia games | |
423 | ||
424 | =head1 SYNOPSIS | |
425 | ||
426 | #!/usr/bin/perl -w | |
427 | use Mafia; | |
428 | ||
429 | player 'Banana Bob', cop, town; | |
430 | player 'Dragon Phoenix', vanilla, townie; | |
431 | player 'Gammie', mafia, goon; | |
432 | player 'gslamm', vanilla, townie; | |
433 | player 'Untrod Tripod', mafia, goon; | |
434 | player 'Werebear', vanilla, townie; | |
435 | player 'willows_weep', town, doctor; | |
436 | ||
437 | day; | |
438 | lynch 'Untrod Tripod'; | |
439 | ||
440 | night; | |
441 | factionkill 'Gammie', 'willows_weep', 'shot'; | |
442 | copcheck 'Banana Bob', 'gslamm'; | |
443 | ||
444 | day; | |
445 | lynch 'Gammie'; | |
446 | ||
447 | night; | |
448 | ||
449 | =head1 DESCRPTION | |
450 | ||
451 | Mafia.pm is a Perl extension for easily moderating Mafia games. You don't even need to know Perl to use it (see L<"WHAT YOU NEED TO KNOW"> for details). | |
452 | ||
453 | =head1 WHAT YOU NEED TO KNOW | |
454 | ||
455 | A typical script starts with the following two lines | |
456 | ||
457 | #!/usr/bin/perl -w | |
458 | use Mafia; | |
459 | ||
460 | The rest of the script is a series of function calls that describe the players and their actions. | |
461 | ||
462 | A function call looks like this: | |
463 | ||
464 | function_name first_argument, second_argument, ... | |
465 | ||
466 | Each argument is either a number, a string (which is a sequence of characters between single or double quotes, such as C<'badguy'>, C<'qwrf'>) or a constant (such as C<mafia>, C<vanilla>, C<bulletproof>). | |
467 | ||
468 | Example calls: | |
469 | ||
470 | player 'Somebody', mafia, goon; # player is the function, 'Somebody' is a string, mafia and goon are constants. | |
471 | lynch 'Nobody'; # lynch is the function, 'Nobody' is a string. | |
472 | day; # day is the function. There are no arguments. | |
473 | ||
474 | =head1 FUNCTIONS | |
475 | ||
476 | =over | |
477 | ||
478 | =item B<player> I<name>, I<trait>, ... | |
479 | ||
480 | Defines a new player named I<name> and its traits (role, faction, role modifiers). | |
481 | ||
482 | Roles: C<vanilla, goon, doctor, vigilante, roleblocker, jailkeeper, gunsmith, tracker, watcher, bodyguard, rolecop, cop, sk, hider>. | |
483 | ||
484 | Factions: C<mafia, town>. C<townie> is a synonim for C<town>. | |
485 | ||
486 | Other attributes: C<miller, godfather, weak, macho, bulletproof> | |
487 | ||
488 | These traits may be specified in any order. | |
489 | ||
490 | Example usage: | |
491 | ||
492 | player 'alice', town, bulletproof, miller, vigilante; # Alice is a NK-Immune Miller Vig | |
493 | player 'bob', town, weak, doctor; # Bob is a Town Weak Doctor | |
494 | player 'eve', mafia, godfather, goon; # Eve is a Mafia Godfather | |
495 | ||
496 | =item B<day> | |
497 | ||
498 | Defines the start of a new Day. All actions in the previous Night are now resolved. | |
499 | ||
500 | =item B<night> | |
501 | ||
502 | Defines the start of a new Night. All actions in the previous Day are now resolved. | |
503 | ||
504 | =item B<lynch> I<player> | |
505 | ||
506 | Notes that I<player> was lynched. | |
507 | ||
508 | =item B<factionkill> I<killer>, I<player>, I<flavour>, [ strong => 1 ] | |
509 | ||
510 | Notes that I<killer> killed I<player> with flavour I<flavour>. Append C<< strong => 1 >> if the kill should ignore roleblocks and doctor/jailkeeper protections. Use this for mafia kills. | |
511 | ||
512 | Example usage: | |
513 | ||
514 | factionkill 'eve', 'alice', 'strangled to death'; | |
515 | factionkill 'eve', 'bob', 'brutally murdered', strong => 1; # This is a strongman kill | |
516 | ||
517 | =item B<protect> I<doctor>, I<player> | |
518 | ||
519 | Notes that I<doctor> protected I<player>. | |
520 | ||
521 | =item B<vig> I<vigilante>, I<player>, I<flavour>, [ strong => 1 ] | |
522 | ||
523 | Notes that I<killer> killed I<player> with flavour I<flavour>. Append C<< strong => 1 >> if the kill should ignore roleblocks and doctor/jailkeeper protections. Use this for Vigilante/Juggernaut kills. | |
524 | ||
525 | Example usage: | |
526 | ||
527 | vig 'chuck', 'bob', 'shot'; | |
528 | vig 'chuck', 'bob', 'shot seven times', strong => 1; # This is a Juggernaut (Strongman Vigilante) kill | |
529 | ||
530 | =item B<roleblock> I<roleblocker>, I<player> | |
531 | ||
532 | Notes that I<roleblocker> roleblocked I<player>. | |
533 | ||
534 | =item B<jailkeep> I<jailkeeper>, I<player> | |
535 | ||
536 | Notes that I<jailkeeper> roleblocked and protected I<player>. | |
537 | ||
538 | =item B<guncheck> I<gunsmith>, I<player> | |
539 | ||
540 | Notes that I<gunsmith> checked if I<player> has a gun. | |
541 | ||
542 | =item B<track> I<tracker>, I<player> | |
543 | ||
544 | Notes that I<tracker> tracked I<player>. | |
545 | ||
546 | =item B<watch> I<watcher>, I<player> | |
547 | ||
548 | Notes that I<watcher> watched I<player>. | |
549 | ||
550 | =item B<guard> I<bodyguard>, I<player> | |
551 | ||
552 | Notes that I<bodyguard> guarded I<player> | |
553 | ||
554 | =item B<rolecopcheck> I<rolecop>, I<player> | |
555 | ||
556 | Notes that I<rolecop> checked the role of I<player> | |
557 | ||
558 | =item B<copcheck> I<cop>, I<player> | |
559 | ||
560 | Notes that I<cop> checked whether I<player> is mafia. | |
561 | ||
562 | =item B<skill> I<SK>, I<player>, I<flavour>, [ strong => 1 ] | |
563 | ||
564 | Notes that I<SK> killed player with flavour I<flavour>. Append C<< strong => 1 >>> if the kill should ignore roleblocks and doctor/jailkeeper protections. Use this for Serial Killer kills. | |
565 | ||
566 | =item B<hide> I<hider>, I<player> | |
567 | ||
568 | Notes that I<hider> hid behind I<player>. | |
569 | ||
570 | =back | |
571 | ||
572 | =head1 AUTHOR | |
573 | ||
574 | Marius Gavrilescu, E<lt>marius@ieval.roE<gt> | |
575 | ||
576 | =head1 COPYRIGHT AND LICENSE | |
577 | ||
578 | Copyright (C) 2013-2015 by Marius Gavrilescu | |
579 | ||
580 | This library is free software; you can redistribute it and/or modify | |
581 | it under the same terms as Perl itself, either Perl version 5.14.2 or, | |
582 | at your option, any later version of Perl 5 you may have available. | |
583 | ||
584 | ||
585 | =cut |