-
Notifications
You must be signed in to change notification settings - Fork 1
/
commands.py
1812 lines (1450 loc) · 60 KB
/
commands.py
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
import sys
import inspect
import urllib2
import json
import namedtuple
import persist
import editor
import dice
from mushyutils import swatch, colorfy, wrap
CommandArgs = namedtuple.namedtuple('CommandArgs', 'name tokens full actor')
"""
General, universal commands are defined here.
Arguments for functions, in parameter 'args' look like this:
(name, tokens, full, actor)
name - command name
tokens - full input, tokenized
full - full input, untokenized
actor - being object who used the command
"""
# These functions need to be identified for the bypass flag
INPUT_BLOCK = set()
MASKABLE = set()
SPECTATORABLE = set()
def spectatorable(func):
"""
Decorate functions that cannot be masked.
"""
if func not in SPECTATORABLE:
SPECTATORABLE.add(func)
return func
def maskable(func):
"""
Decorate functions that cannot be masked.
"""
if func not in MASKABLE:
MASKABLE.add(func)
return func
def block(func):
"""
Decorate functions that require input blocking immediately after execution.
"""
if func not in INPUT_BLOCK:
INPUT_BLOCK.add(func)
return func
def zap(args):
"""
Allows the DM to force-disconnect another user. Can be used if there
are errors regarding a user, or if someone misbehaves.
Note: The zapped player's data is not saved!
syntax: zap <player>
"""
if not args.actor.dm:
return False
elif len(args.tokens) < 2:
args.actor.sendMessage("Usage: zap <player>")
return True
if args.tokens[1] in args.actor.session:
try:
target = args.actor.session.getEntity(args.tokens[1])
target.proxy.running = False
target.session.remove(target)
target.proxy.kill()
args.actor.sendMessage("Disconnected " + target.name + ".")
except:
args.actor.sendMessage("Error while disconnecting user " + target.name + ".")
return True
def alias(args):
"""
Players may wish to create their own shortcuts, called "aliases"
to allow easier playing. This is particularly useful if one repeats
a non-trivial command often, such as a complex emote or roll. Up to
20 aliases may be stored at once.
syntax: alias <shortcut> <command>
unalias <shortcut>
Users may check their list of aliases with the "aliases" command.
"""
if args.tokens[0] == "unalias":
if len(args.tokens) < 2:
return False
key = args.tokens[1]
if key in args.actor.aliases:
del args.actor.aliases[key]
args.actor.sendMessage('Alias "' + key + '" removed.')
persist.saveEntity(args.actor)
else:
args.actor.sendMessage('Alias "' + key + '" does not exist.')
elif args.tokens[0] == "aliases" or (args.tokens[0] == "alias" and len(args.tokens) == 1):
if len(args.actor.aliases) == 0:
args.actor.sendMessage("You have no saved aliases.")
return True
msg = "List of registered aliases:\n"
for key in args.actor.aliases:
msg += key + ": " + args.actor.aliases[key] + "\n"
args.actor.sendMessage(msg)
else:
if len(args.tokens) < 3:
return False
elif len(args.actor.aliases) >= 20:
args.actor.sendMessage("You may only have up to 20 aliases saved at once.")
return True
key = args.tokens[1]
alias_cmd = args.full[len(args.tokens[0] + args.tokens[1]) + 2:]
args.actor.aliases[key] = alias_cmd
args.actor.sendMessage('Alias "' + key + '" added.')
persist.saveEntity(args.actor)
return True
@spectatorable
def configure(args):
"""
Players have several things they can customize for output. Players may
configure these settings by using the "configure" command.
syntax: configure <setting> [value]
Below are a list of configurable options:
wrap <number> - number of columns per line, 0 = unwrapped (default:)
saywrap - wraps say output, toggle (default: off)
"""
if len(args.tokens) < 2:
return False
setting = args.tokens[1]
if setting in ("wrap", "cols"):
try:
if len(args.tokens) < 3:
args.actor.sendMessage("Usage: configure wrap <number>")
return True
args.actor.settings["cols"] = int(args.tokens[2])
args.actor.sendMessage("Lines will now wrap at " + args.tokens[2] + " characters.")
except Exception:
args.actor.sendMessage("Value must be a number (0 for unwrapped).")
elif setting == "saywrap":
args.actor.settings["saywrap"] = not args.actor.settings["saywrap"]
if args.actor.settings["saywrap"]:
args.actor.sendMessage("Turned say wrapping " + colorfy("ON", "green"))
else:
args.actor.sendMessage("Turned say wrapping " + colorfy("OFF", "green"))
else:
args.actor.sendMessage("That is not a valid setting. Check help settings for more details.")
persist.saveEntity(args.actor)
return True
def language(args):
"""
Players may speak in a variety of different languages, so long as
they have the OK from the DM. This command allows a player to list
the languages that they know, or if they are a DM, register a
language to a player. Languages are persistant.
This ties in heavily with the say/whisper command.
syntax: language <command>
List of subcommands and syntax:
list: language list
peek: language peek <target>
learn: language learn <target> <language>
forget: language forget <target> <language>
"""
if len(args.tokens) < 4:
if len(args.tokens) == 1:
if args.tokens[0] != 'languages':
return False
elif len(args.tokens) == 2:
if args.tokens[1] != 'list':
return False
elif len(args.tokens) == 3:
if args.tokens[1] != 'peek':
return False
else:
return False
tokens = args.tokens
# tallies substitution
if args.tokens[0] == 'languages':
tokens = ['language', 'list']
subcommand = tokens[1]
target = ''
language = ''
div = ", "
if len(tokens) > 2:
target = tokens[2]
target = target[0].upper() + target[1:]
if len(tokens) > 3:
language = tokens[3]
language = language.lower()
if subcommand == 'list':
args.actor.sendMessage("You know the following languages: " + div.join(args.actor.languages))
elif subcommand == 'peek' and target in args.actor.session:
e = args.actor.session.getEntity(target)
args.actor.sendMessage(target + " knows the following languages: " + div.join(args.actor.languages))
elif subcommand in ('learn', 'register') and target in args.actor.session:
e = args.actor.session.getEntity(target)
e.languages.append(language)
args.actor.sendMessage(target + " now understands the language: " + colorfy(language, "green"))
e.sendMessage("You have learned the language: " + colorfy(language, "green"))
persist.saveEntity(e)
elif subcommand in ('forget', 'unregister') and target in args.actor.session:
e = args.actor.session.getEntity(target)
if language in e.languages:
e.languages.remove(language)
args.actor.sendMessage(target + " has forgotten the language: " + colorfy(language, "green"))
e.sendMessage("You have forgotten the language: " + colorfy(language, "green"))
else:
args.actor.sendMessage(target + " does not know the language " + language + ".")
else:
return False
return True
@spectatorable
def help(args):
"""
Check these helpfiles for something.
syntax: help <subject>
subject - the subject or command which you want to check for help
Known bug: Doesn't always play nice with column wrapping.
"""
# This would normally be circular, but this is an exceptional case
import functionmapper
# generate the docstring mapping first
docs = {}
functions = inspect.getmembers(sys.modules[__name__], inspect.isfunction)
for functionName, function in functions:
docs[functionName] = function.__doc__
if len(args.tokens) == 1:
commands = functionmapper.commandFunctions.keys()
commands.sort()
msg = " "
i = 0
for command in commands:
if command not in docs or docs[command] is None:
continue
msg = msg + command + (' ' * (15 - len(command)))
i = (i + 1) % 4
if i == 0:
msg = msg + "\n "
args.actor.sendMessage("There are help files on the following commands.\nType help <command> for details.")
args.actor.sendMessage(msg)
return True
docstring = None
if args.tokens[1] in docs:
docstring = docs[args.tokens[1]]
if docstring is None:
args.actor.sendMessage("There is no helpfile for " + args.tokens[1] + ".")
else:
prelude = "help file for: " + args.tokens[1] + "\n" + ("-" * len("help file for: " + args.tokens[1]))
prelude = colorfy(prelude, 'green')
args.actor.sendMessage(prelude + docstring)
return True
def _speak(args, second_tense, third_tense, speak_color):
"""
Internal function used by say, whisper, and yell.
second_tense - You say/whisper/yell
third_tense - He says/whispers/yells
"""
if len(args.tokens) < 2:
return False
full = args.full
# Get rid of the "say" command token
rest_tokens = args.tokens[1:]
full = full[len(args[0]) + 1:]
target_entity = None
lang = None
color_lang = None
# CASE 1: Match on SAY IN
if rest_tokens[0].lower() == 'in' and len(rest_tokens) >= 3:
lang = rest_tokens[1].lower()
if lang in args.actor.languages:
# cut out the "in lang" portion
full = full[len(rest_tokens[0]) + len(rest_tokens[1]) + 2:]
rest_tokens = rest_tokens[2:]
else:
lang = None
# see if there's a target in here
if len(rest_tokens) >= 3 and rest_tokens[0].lower() == 'to':
target_entity = args.actor.session.getEntity(rest_tokens[1])
if target_entity is not None:
full = full[len(rest_tokens[0]) + len(rest_tokens[1]) + 2:]
rest_tokens = rest_tokens[2:]
# CASE 2: Match on SAY TO
elif len(rest_tokens) >= 3 and rest_tokens[0].lower() == 'to':
target_entity = args.actor.session.getEntity(rest_tokens[1])
if target_entity is not None:
full = full[len(rest_tokens[0]) + len(rest_tokens[1]) + 2:]
rest_tokens = rest_tokens[2:]
# Check for language
if len(rest_tokens) >= 3 and rest_tokens[0].lower() == 'in':
lang = rest_tokens[1].lower()
if lang in args.actor.languages:
# cut out the "in lang" portion
full = full[len(rest_tokens[0]) + len(rest_tokens[1]) + 2:]
rest_tokens = rest_tokens[2:]
else:
lang = None
if target_entity == args.actor:
args.actor.sendMessage("Stop speaking to yourself!")
return True
# Properize the remaining text
full = full[0].upper() + full[1:]
if full[-1] not in ('.', '!', '?'):
full = full + '.'
if lang is not None:
color_lang = colorfy(lang, "green")
# A tricky runtime decoration of Entity.sendMessage, which wraps depending on settings
def sendMessage(target, text):
if target.settings["saywrap"]:
text = wrap(text, cols=60, indent=" ")
target.sendMessage(text)
# Say stuff to a target
if target_entity is not None:
for e in args.actor.session.getAllEntities():
# in a language
if lang is not None:
if e == args.actor:
sendMessage(e, colorfy('You ' + second_tense + ' to ' + target_entity.name + ' in ' + color_lang + ', "' + full + '"', speak_color))
elif e == target_entity:
if lang in target_entity.languages or e.spectator:
sendMessage(e, colorfy(args.actor.name + ' ' + third_tense + ' to you in ' + color_lang + ', "' + full + '"', speak_color))
else:
e.sendMessage(colorfy(args.actor.name + ' ' + third_tense + ' something to you in ' + color_lang + '.', speak_color))
else:
if lang in e.languages or e.spectator:
sendMessage(e, colorfy(args.actor.name + ' ' + third_tense + ' to ' + target_entity.name + ' in ' + color_lang + ', "' + full + '"', speak_color))
else:
e.sendMessage(colorfy(args.actor.name + ' ' + third_tense + ' something to ' + target_entity.name + ' in ' + color_lang + '.', speak_color))
# common
else:
if e == args.actor:
sendMessage(e, colorfy('You ' + second_tense + ' to ' + target_entity.name + ', "' + full + '"', speak_color))
elif e == target_entity:
sendMessage(e, colorfy(args.actor.name + ' ' + third_tense + ' to you, "' + full + '"', speak_color))
else:
sendMessage(e, colorfy(args.actor.name + ' ' + third_tense + ' to ' + target_entity.name + ', "' + full + '"', speak_color))
# Say stuff to everyone
else:
for e in args.actor.session.getAllEntities():
# in a language
if lang is not None:
if e == args.actor:
sendMessage(e, colorfy('You ' + second_tense + ' in ' + color_lang + ', "' + full + '"', speak_color))
elif lang in e.languages or e.spectator:
sendMessage(e, colorfy(args.actor.name + ' ' + third_tense + ' in ' + color_lang + ', "' + full + '"', speak_color))
else:
e.sendMessage(colorfy(args.actor.name + ' ' + third_tense + ' something in ' + color_lang + '.', speak_color))
# common
else:
if e == args.actor:
sendMessage(e, colorfy('You ' + second_tense + ', "' + full + '"', speak_color))
else:
sendMessage(e, colorfy(args.actor.name + ' ' + third_tense + ', "' + full + '"', speak_color))
return True
@maskable
def say(args):
"""
Say something out loud, in character. Unless otherwise specified, things
are said in common.
syntax: say [modifiers] <message>
Modifiers can be lanaguage, or target. These are expressed naturally
and the order does not matter. See below for examples
examples:
say hello
>> Eitan says, "Hello."
say in elven "such a snob"
>> Eitan says in elven, "Such a snob."
say to king Hello your majesty.
>> Eitan says to King, "Hello your majesty."
say in dwarven to Gimli Sup brosef?
>> Eitan says to Gimli in dwarven, "Sup, brosef?"
say to Legolas in elven You're one pretty elf!
>> Eitan says to Legolas in elven, "You're one pretty elf!"
Alternatively, as a shorthand, you may start the say command with the "'" token (no space).
example:
'Hello, everyone!
>>Eitan says, "Hello, everyone!"
"""
if args.full[-1] == '?':
return _speak(args, 'ask', 'asks', 'white')
elif args.full[-1] == '!':
return _speak(args, 'exclaim', 'exclaims', 'white')
return _speak(args, 'say', 'says', 'white')
@maskable
def whisper(args):
"""
Works just like "say", just with whisper flavor. For more info,
check "help say".
syntax: whisper [modifiers] <message>
"""
return _speak(args, 'whisper', 'whispers', 'dgray')
@maskable
def yell(args):
"""
Works just like "say", just with yell flavor. For more info,
check "help say".
syntax: yell [modifiers] <message>
"""
return _speak(args, 'yell', 'yells', 'byellow')
def pm(args):
"""
Give a private message to someone, out of character. Other people
cannot see these messages.
Note: This is OOC and shouldn't be abused!
syntax: pm <player> <message>
player - target of the pm
message - what you would like to say privately
"""
if not len(args.tokens) >= 3:
return False
if args.tokens[1] in args.actor.session:
target = args.actor.session.getEntity(args.tokens[1])
target.sendMessage(colorfy("[" + args.actor.name + ">>] " +
args.full[len(args.tokens[0] + " " + args.tokens[1] + " "):], 'purple'))
args.actor.sendMessage(colorfy("[>>" + target.name + "] " +
args.full[len(args.tokens[0] + " " + args.tokens[1] + " "):], 'purple'))
return True
@spectatorable
def who(args):
"""
See who is connected to the MUSH server.
syntax: who
"""
msg = colorfy("Currently connected players:\n", "cyan")
for e in args.actor.session.getAllEntities():
name = colorfy(e.name, "cyan")
if e.dm:
name = name + colorfy(" (DM)", "bright red")
elif e.spectator:
name = name + colorfy(" (S)", "bright yellow")
msg = msg + " " + name + "\n"
args.actor.sendMessage(msg)
return True
@spectatorable
def logout(args):
"""
Logs you out of the game.
syntax: logout
"""
args.actor.sendMessage(colorfy("[SERVER] You have quit the session.", "bright yellow"))
args.actor.session.broadcastExclude(colorfy("[SERVER] " + args.actor.name + " has quit the session.", "bright yellow"), args.actor)
persist.saveEntity(args.actor)
try:
args.actor.proxy.running = False
args.actor.session.remove(args.actor)
args.actor.proxy.kill()
except:
return True
return True
@maskable
def emote(args):
"""
Perform an emote. Use the ";" or "*" token as a placeholder for your name.
syntax: emote <description containing ; somewhere>
example:
emote In a wild abandon, ; breaks into a fit of giggles.
>> In a wild abandon, Eitan breaks into a fit of giggles.
emote Smoke drifts upwards from a pipe held between ;'s lips.'
>> Smoke drifts upwards from a pipe held between ;'s lips.'
If no semicolon is found, your name will be stuck at the start:
emote yawns lazily.
>> Eitan yawns lazily.
Alternatively, as a shorthand, you may start an emote with the ";" token
(with no space).
example:
;laughs heartedly
>> Eitan laughs heartedly.
"""
if len(args.tokens) < 2:
return False
marking = ">"
rest = args.full[len(args.name + " "):]
if not ';' in args.full:
rest = args.actor.name + " " + rest
else:
rest = rest.replace(';', args.actor.name)
args.actor.session.broadcast(colorfy(marking + rest, "dark gray"))
return True
def ooc(args):
"""
Broadcast out of character text. Anything said here is OOC.
syntax: ooc <message>
example:
ooc Do I need to use a 1d6 or 1d8 for that, DM?
>> [OOC Justin]: Do I need to use a 1d6 or 1d8 for that, DM?
Alternatively, as a shorthand, you may start an ooc message with the
"%" token (no space).
example:
%If you keep metagaming, I'm going to kill you!
>> [OOC DM_Eitan]: If you keep metagaming, I'm going to kill you!
"""
if len(args.tokens) < 2:
return False
name = args.actor.name
if args.actor.dm:
name = name + " (DM)"
marking = "[OOC " + name + "]: "
marking = colorfy(marking, "bright red")
rest = args.full[len(args.name + " "):]
args.actor.session.broadcast(marking + rest)
return True
def roll(args):
"""
Roll a dice of a specified number of sides. The dice roll is public.
syntax: roll <sequence> [reason]
example:
roll 1d20
roll 2d6 + 2 "to hit"
There are some limitations on dice.
You cannot roll over 20 dice at once (ex: 99d20)
You cannot roll a die with over 100 sides (ex: 1d200)
You cannot roll less tha one die, or a die with less than two sides
The number of dice, and the number of sides, must be numerical
Alternatively, as a shorthand, you may roll a single die of N sides
with no specified purpose with the "#" token (no space). Standard 1d20
rolls can be invoked by simply using the "roll" command with no arguments.
example:
#6 is the same as roll 1d6
roll is the same as roll 1d20
Rolls can also be kept hidden from others. To do this, use the
command "hroll" instead of "roll". DMs can make a special hidden
roll that other players can't see, but still know happened, by using
the "droll" command.
example:
hroll 1d20
"""
if len(args.tokens) == 1:
new_tokens = [args.name, "1d20"]
new_full = args.name + " 1d20"
newargs = CommandArgs(name=args.name, tokens=new_tokens, full=new_full, actor=args.actor)
args = newargs
elif len(args.tokens) < 2:
return False
reason_index = args.full.find('"')
reason = ""
if reason_index == -1:
reason_index = len(args.full)
else:
reason = args.full[reason_index+1:-1]
dice_str = args.full[len(args.tokens[0])+1:reason_index]
visible = True
if args.tokens[0] in ('hroll', 'droll'):
visible = False
if args.tokens[0] == 'droll' and not args.actor.dm:
return False
result = ""
msg = ""
try:
result, msg = dice.parse(dice_str)
except dice.DiceException as e:
e_msg = 'Bad roll formatting! The clause ' + colorfy(str(e), "bred") + " is no good!"
e_msg += '\nFor more help, try ' + colorfy("help roll", "green") + '.'
args.actor.sendMessage(e_msg)
return True
pre = args.actor.name + " "
if not visible:
pre += colorfy("secretly ", "bred")
pre += "rolls " + colorfy(dice_str, "yellow")
if reason != "":
pre += "for " + colorfy(reason, "bred")
pre += ".\n "
msg = pre + msg + " (total = " + colorfy(str(result), "yellow") + ")"
if visible:
args.actor.session.broadcast(msg)
else:
args.actor.sendMessage(msg)
if args.tokens[0] == 'droll':
args.actor.session.broadcastExclude(colorfy("The DM makes a hidden roll.", "bred"), args.actor)
return True
def fudge(args):
result, out = dice.fudge()
msg = args.actor.name + " rolls the dice: " + out + " (total = " + result + ")"
args.actor.session.broadcast(msg)
return True
def mask(args):
"""
Mask a command as if you were another character. Don't abuse this, DM!
syntax: mask <name> <command>
name - the name of the entity you wish to do something as
command - the regular command string as if you were entering it
as normal
example:
mask King say Welcome, my subjects, to my domain!
>> King says, "Welcome, my subjects, to my domain!"
mask John ;bows with a flourish.
>> John bows with a flourish.
Alternatively, as a shorthand, you may mask as another person by using
the "$" token (no space).
example:
$Nameless say Who... who am I?
>> Nameless says, "Who... who am I?"
Unfortunately, this can get annoying. You can set masks indefinitely by using
the command "mask <name>". This will mask ALL commands until the DM uses the
command "mask clear".
"""
# Exceptional case where we need this for husk-command-trickery
import commandparser
import functionmapper
import entity
if len(args.tokens) == 1 and args.tokens[0] == "unmask":
newargs = CommandArgs("mask", ["mask", "clear"], "mask clear", args.actor)
args = newargs
elif len(args.tokens) == 2:
if args.tokens[1] in ("clear", "reset", "remove"):
args.actor.mask = None
args.actor.sendMessage("You take off your mask.")
else:
huskname = args.tokens[1][0].upper() + args.tokens[1][1:]
husk = entity.Entity(name=huskname, session=args.actor.session)
husk.languages = args.actor.languages
args.actor.mask = husk
args.actor.sendMessage("You put on a " + colorfy(huskname, "green") + " mask.")
return True
elif not args.actor.dm:
args.actor.sendMessage("Whoa there... This is a DM power! Bad!")
return True
else:
return False
new_full = args.full[len(args.tokens[0] + " " + args.tokens[1] + " "):]
new_tokens = new_full.split(" ")
if functionmapper.commandFunctions[new_tokens[0]] not in MASKABLE:
args.actor.sendMessage("That command cannot be masked.")
return True
husk = entity.Entity(name=args.tokens[1][0].upper() + args.tokens[1][1:], session=args.actor.session)
husk.languages = args.actor.languages
commandparser.CommandParser().parseLine(new_full, husk)
return True
def display(args):
"""
Display text in a color of your choosing without any "tags". Basically,
use this as an immersion tool for typing descriptions.
The DM may want to display text to only a particular player, and not want
the rest of the group to be aware OOCly, allowing for better roleplay.
syntax: display [identifier] <text>
You may display things in a color, to a specific target, or both. It is
sufficient to supply the name of the color or target. If both are
specified, the syntax becomes:
color@target
To view a list of colors, type "colors". If the supplied color does not
exist, the default is used. If the dual-identifier form is used, a target
must be specified. The keyword "all" may be specified as well.
example:
display bred The flame licks at the prisoner's cheek.
>> \033[1;31mThe flame licks at the prisoner's cheek.\033[0m
display yellow@justin Spiritual voices wail in your mind...
>> \033[0;33mSpiritual voices wail in your mind...\033[0m
Alternatively, as a shorthand, having the "@" symbol in the command name
is sufficient for sending the display command. Both a color and a target
must be specified.
example:
red@all Blood trickles down your nose.
>> \033[0;31mBlood trickles down your nose.\033[0m
"""
if len(args.tokens) < 2:
return False
color = "default"
target = None
rest = args.full[len(args.tokens[0]) + 1:]
if '@' not in args.tokens[1]:
# Single token, either a color or an entity
if args.tokens[1] in args.actor.session:
target = args.actor.session.getEntity(args.tokens[1])
rest = rest[len(args.tokens[1]) + 1:]
elif args.tokens[1].lower() in swatch:
color = args.tokens[1].lower()
rest = rest[len(args.tokens[1]) + 1:]
else:
params = args.tokens[1].split("@")
rest = rest[len(args.tokens[1]) + 1:]
if params[0].lower() in swatch:
color = params[0].lower()
if params[1] in args.actor.session:
target = args.actor.session.getEntity(params[1])
elif params[1].lower() != "all":
args.actor.sendMessage("There is no person named " + params[1] + ".")
return True
rest = colorfy(rest, color)
if target is not None:
target.sendMessage(rest)
args.actor.sendMessage("You send " + target.name + ": " + rest)
else:
args.actor.sendMessage("You send everyone: " + rest)
args.actor.session.broadcastExclude(rest, args.actor)
return True
def status(args):
"""
Set your status. This is meant to be an in-character roleplay tool. For
example, after being struck with an arrow, you may want to set your status
to indicate that you are injured. Treat these as an emoted "state".
Setting these is not quiet, and will indicate to the group what is going on
as an emote. Take care to phrase the status as a passive state. It sounds
best if you are able to say "Soandso is..." prior to a status.
If you specify status as "clear", it will clear your status silently.
syntax: status <status>
example:
status Limping behind the group, using his staff as a cane.
>> Eitan is limping behind the group, using his staff as a cane.
>> glance Eitan
>> Eitan is limping behind the group, using his staff as a cane.
>> status clear
"""
if len(args.tokens) < 2:
return False
if args.tokens[1] == 'clear':
args.actor.status = ""
args.actor.sendMessage("You've cleared your status.")
return True
status = args.full[len(args.tokens[0] + " "):]
status = status[0].upper() + status[1:]
if status[-1] not in (".", "!", "?"):
status = status + "."
args.actor.status = status
status = status[0].lower() + status[1:]
args.actor.sendMessage(colorfy(">You are " + status, "dark gray"))
args.actor.session.broadcastExclude(colorfy(">" + args.actor.name + " is " + status, "dark gray"), args.actor)
return True
@spectatorable
def glance(args):
"""
Glance at another player to see their set status.
syntax: glance <player>
"""
if len(args.tokens) < 2:
return False
if args.tokens[1] in args.actor.session:
target = args.actor.session.getEntity(args.tokens[1])
args.actor.sendMessage("You glance at " + target.name + ".")
if target.status == "":
return True
status = target.status[0].lower() + target.status[1:]
args.actor.sendMessage(" " + target.name + " is " + colorfy(status, "dark gray"))
else:
args.actor.sendMessage('There is no player "' + args.tokens[1] + '" here.')
return True
@spectatorable
def examine(args):
"""
Examines another player's profile.
syntax: examine <player>
"""
if len(args.tokens) != 2:
return False
if args.tokens[1] in args.actor.session:
target = args.actor.session.getEntity(args.tokens[1])
top = target.name + "'s Profile"
args.actor.sendMessage(top)
args.actor.sendMessage("-"*len(top))
if target.facade is not None and target.facade != "":
args.actor.sendMessage(target.facade)
else:
args.actor.sendMessage(target.name + "'s profile is empty.")
return True
@spectatorable
def colors(args):
"""
Displays a list of the colors available for use in certain commands.
syntax: colors
"""
msg = """ List of colors:
\033[0mDEFAULT\033[0m
\033[1;37mWHITE\033[0m
\033[0;37mBGRAY\033[0m
\033[1;30mDGRAY\033[0m
\033[0;30mBLACK\033[0m
\033[0;34mBLUE\033[0m
\033[1;34mBBLUE\033[0m
\033[0;36mCYAN\033[0m
\033[1;36mBCYAN\033[0m
\033[0;32mGREEN\033[0m
\033[1;32mBGREEN\033[0m
\033[0;33mYELLOW\033[0m
\033[1;33mBYELLOW\033[0m
\033[0;31mRED\033[0m
\033[1;31mBRED\033[0m
\033[0;35mPURPLE\033[0m
\033[1;35mBPURPLE\033[0m
"""
args.actor.sendMessage(msg)
return True
def paint(args):
"""
A DM may "paint" the current scene of the session for players to
view. Two things may be painted in a scene.
"Scene" title - The name of the scene
"Scene" body - The description of the scene
Players may look at the scene by using the "look" command with no
arguments. The color of the painted text is dictated by the brush
setting. For information on setting a color, check "help brush".
syntax: paint <title | body> <description>
To view a list of colors, type "colors".
example:
paint title Inferno Cave
paint body Lava swirls around burning stone in a river of red.
"""
if len(args.tokens) < 3:
return False